I am trying to practice my algorithm skills. I know there is already an algorithm written out there, I just want to try it on my own and see how close i could get.
INPUT:
arr1 = ['asd','ew','lol','asd']
EXPECTED OUTPUT:
{ asd: 2, ew: 1, lol: 1 }
This is my code:
arr1 = ['asd', 'ew', 'lol', 'asd']
arr2 = []
results = {}
function checkIfExists(word) {
if (arr2.length != 0) {
for (i = 0; i < arr2.length; i++) {
if (arr2[i] == word) {
results[word] += 1
} else {
arr2.push(word)
results[word] = 1
}
}
} else {
arr2.push(word)
results[word] = 1
}
}
for (i = 0; i < arr1.length; i++) {
checkIfExists(arr1[i])
}
console.log(results)
ACTUAL OUTPUT:
{ asd: 2, ew: 2 }
You used i as a global variable, so don't use for two loop. Other mistake is in your increment algorithm that add more than needed count to results array. So try it:
arr1 = ['asd','ew','lol','asd']
arr2 = []
results = {}
function checkIfExists(word){
if (arr2.length != 0){
var exists = false;
for (var j = 0; j < arr2.length; j++){
if(arr2[j] == word){
results[word] += 1
exists = true;
break;
}
}
if(!exists) {
arr2.push(word)
results[word] = 1
}
}else{
arr2.push(word)
results[word] = 1
}
}
for (var i = 0; i < arr1.length; i++) {
checkIfExists(arr1[i])
}
console.log(results)
There are three questions I identified immediately which you might have intended to ask.
What's wrong with my code?
I couldn't phrase this better than Felix Klings comment:
The first problem is that you are not using var to declare i.
The loop presumably intended to locate an item within arr2 also modifies arr2... a lot!
Why is my code producing incorrect output? Your code is actually producing correct output for the logic it expresses. I suspect the issue is that the logic it expresses doesn't match the logic you intended to express. This isn't uncommon
How do I fix my code?
Start by changing your loop idioms from for (i = ...) to for (var i = ...).
Think about the purpose of that loop. If it's intended to locate an item within arr2, then it shouldn't need to modify arr2 to do so. Perhaps you don't need the loop;
You could probably use Array.prototype.indexOf or Array.prototype.includes in place of that entire loop!
You could probably use function checkIfExists(word) { results[word] = arr1.filter(function(w) { return w === word; }).length; } in place of that entire function!
It seems like you could use some higher-level awareness when designing functions, so perhaps it might be a good idea to try to wrap your head around some of the elements in this code:
var arr1 = ['asd','ew','lol','asd'];
var result = arr1.reduce(function(result, w) { result[w] = result[w] || 0;
result[w]++;
return result; }, {}));
console.log(result);
Related
I am trying to make a multiplayer poker game in Node.js, and I've been having a lot of issues lately. This is a major one. This code is supposed to identify a Straight hand from an array. However, my code apparently isn't universal. I made 2 arrays as test cases and different results are produced when even just of the arrays is identified as a straight. Please help.
Here's the code:
var arr = [9,1,2,11,8,12,10]; // first array
var arr2 = [9,1,8,4,5,3,2]; // second array
var straight = [];
// Removes duplicate elements in an array
/* example:
array = removeDuplicates(array)
*/
function removeDuplicates(arr){
let unique_array = []
for(let i = 0;i < arr.length; i++){
if(unique_array.indexOf(arr[i]) == -1){
unique_array.push(arr[i])
}
}
return unique_array
}
//Sorts the array
arr.sort(function(a,b){return b-a});
//Removes duplicates
arr = removeDuplicates(arr);
// Displays sorted and cleaned up array
console.log(arr)
/*Basic translation: loops through the array
and if the difference between the a term and
the term after it is 1, it will append it to the
array 'straight'. It will break if the difference
is greater than 1. Then it will remove those elements
from the original array and retry to append consecutive
elements in the 'straight' array.
*/
for (var i=1; i<arr.length+1; i++) {
if (arr[i-1] - arr[i] === 1) {
straight.push(arr[i-1],arr[i]); // error occurs at this line
} else if (arr[i-1] - arr[i] > 1){
break; }
if (straight.length === 2) {
arr.splice(arr.indexOf(straight[0]),1)
arr.splice(arr.indexOf(straight[1]),1)
straight = [];
for (var i=1; i<arr.length; i++) {
if (arr[i-1] - arr[i] === 1) {
straight.push(arr[i-1],arr[i]);
}
}
}
};
// There are duplicates and I don't know why sometimes
straight = removeDuplicates(straight)
console.log(straight);
This doesn't work for some reason. But it will work fine ONLY for the first array if you change
straight.push(arr[i-1],arr[i]);
to
straight.push(arr[i-1],arr[i],arr[i]);
It works ONLY for the second array if you switch the variable names:
var arr2 = [9,1,2,11,8,12,10]; // first array
var arr = [9,1,8,4,5,3,2]; // second array
and run the code without further changes, I don't know why it does this. I even went as far as logging the boolean
arr[i-1] - arr[i] === 1
to the console (in the loop, I mean), and it comes out true four times in a row (going through the first 5 indexes of the array), so I don't know why it stops at 11 for the first array and decides 11-10 isn't 1.
your logic is a bit hard to follow - I think the issue you're seeing is due to clearing the straight array in the if (straight.length === 2) part. Here's my shot at simplifying things:
const isStraight = a => {
const uniq = a.filter((val, idx) => a.indexOf(val) === idx);
uniq.sort((a, b) => a-b);
const tries = uniq.length - 4;
for (var i=0; i<tries; i++) {
if (uniq[i + 4] - uniq[i] === 4) {
return true;
}
}
return false;
}
console.log(isStraight([9,1,2,11,8,12,10]));
console.log(isStraight([9,1,8,4,5,3,2]));
console.log(isStraight([2,5,4,3,6,8,7]));
console.log(isStraight([2,5,4,3,6,8,7,10]));
console.log(isStraight([2,5,2,4,7,3,6,8,8,8,8,7]));
console.log(isStraight([1,2,3,4,6,7,8,9,11,12,13,13]))
let arr = [9,1,2,11,8,12,10];
function checkStraight(arr) {
let answer = [];
if(arr.length < 5)
return false;
arr = [...new Set(arr)];
arr.sort(function(a,b){return b-a});
for(let index=0; index < arr.length; index++){
if(answer.length === 5) break;
if(answer.length === 0){
answer.push(arr[index])
}
if(answer[answer.length-1] - arr[index] === 1){
answer.push(arr[index]);
}else{
answer = [];
answer.push(arr[index])
}
}
return answer
}
console.log(checkStraight(arr));
You can try to run thru the code, should be quite simple. Basically instead of comparing between elements inside own array, we compare between two array, and conditionally push the matched straight card into new array
**assumptions: ** Since we are playing poker, assuming once 5 consecutive card been found, it's consider a straight and no further checking needed
I'm trying to practice for an interview and found a challenge online to write a function that will take an array of numbers and only return values that exist just once in the array, and return those values in order. For example, the array [1, 3, 5, 6, 1, 4, 3, 6] should return [4, 5].
I have a script that is passing the tests but for some of the tests is running too slow. Am I going about this wrong? Is there some fundamental way to speed this up? The script starts with findTheNumbers, and a is the array input:
function findTheNumbers(a) {
var retVal = [];
var nonUnique = [];
for (var i = 0; i < a.length; i++){
var isUnique = true;
if (i != 0){
for (var j = 0; j < nonUnique.length; j++){
if (a[i] == nonUnique[j]){
isUnique = false;
break;
}
}
}
if (isUnique){
for (var k = 0; k < a.length; k++){
if (a[i] == a[k] && i != k){
isUnique = false;
nonUnique.push(a[i]);
break;
}
}
}
if (isUnique){
retVal.push(a[i]);
if (retVal.length == 2){
break;
}
}
}
retVal = sortArrayOfLengthOfTwo(retVal);
return retVal;
}
function sortArrayOfLengthOfTwo(array){
var retVal = [];
if (array[0] > array[1]){
retVal.push(array[1]);
retVal.push(array[0]);
} else {
retVal = array;
}
return retVal;
}
UPDATE -
Not sure where the best place for this is, but here is my new version based on the accepted answer's hints (which worked SO much faster):
function findTheNumbers(a) {
var retVal = [];
var dict = {};
for (var i = 0; i < a.length; i++){
dict[a[i]] = 1 + (dict[a[i]] || 0);
}
for (var key in dict){
if (dict[key] == 1){
retVal.push(parseInt(key));
}
}
return retVal;
}
When a program is too slow, the rule of thumb is to
first blame your algorithm
next blame your implementation (edit: as suggested by Bergi)
finally blame the language
In your case, you parse the whole array n times, that is, the complexity is o(n^2). You can do the same with only one full parse, and a dictionary of the count for each element.
Edit about your new algorithm
Your new algorithm is very good. For the sake of your interview I have some comments:
instead of var, you should consider using const as much as possible, and let otherwise
do not hesitate to put intermediate results within variables
use more descriptive names for your variables
Here's the way I would implement it:
function findTheUniqueNumbers(a) {
const numberCounts = {} // I prefer "new Map()" on recent env.
a.forEach( function(e) {
const previousCount = numberCounts[e] || 0
numberCounts[e] = previousCount + 1
} )
return Object.keys(numberCounts)
.filter( function(e) {
return numberCounts[e] === 1
} )
}
const uniqueNumber = findTheUniqueNumbers([1, 3, 5, 6, 1, 4, 3, 6])
console.log(uniqueNumber)
i'm spending way too much time on trying to figure these 2 codes out.
i'm too burn out. i spent the last 2 hrs and lots of hours trying to figure out why Code 1 doesn't accept. And for Code 2, it accepted, but i'm not too sure.
Code 1.
Given an input Object, how might we loop over the Object IN REVERSE and
print its values using console.log()?
function printObjectValuesInReverse(object) {
var myArray = [];
for(var keys in object) {
myArray.push(object[keys]);
}
for(var i = keys.length-1; i >= 0; i--) {
console.log(keys[i]);
}
Code 2
Given an Array of Strings and a Function designed to test the String in some way and return a Boolean on whether it passed, return true if ALL Strings pass the test.
function allStringsPass(strings, test) {
for (var i =0; i < strings.length; i++) {
if(test(strings[i]) === false) {
return false;
}
}
return true;
You can simply use reverse function. so your console.log will be console.log(myArray.reverse())
Update if you dont want to use a reverse function, your code should be like this
function printObjectValuesInReverse(object) {
var myArray = [];
for(var keys in object) {
myArray.push(object[keys]);
}
for(var i = myArray.length-1; i >= 0; i--) {
console.log(myArray[i]); // I changed this from keys[i]
}
}
printObjectValuesInReverse({a: 1, b: 2, c: 3})
Code 1
keys isn't an array. I think you might wanna iterate over myArray.
function printObjectValuesInReverse(object) {
var myArray = [];
for(var keys in object) {
myArray.push(object[keys]);
}
for(var i = myArray.length-1; i >= 0; i--) {
console.log(myArray[i]);
}
}
Code 2
Just but the closing brace and it should work fine:
function allStringsPass(strings, test) {
for (var i =0; i < strings.length; i++) {
if(test(strings[i]) === false) {
return false;
}
}
return true;
}
I've been trying the Number.isInteger() method on chrome console.
and after doing a for loop and checking the result with the console.log(arr); I'm getting an array with only one value of 1. like this [1];
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (Number.isInteger(arr[i])) {
arr.splice(arr.indexOf(arr[i], 1));
}
}
Any one have an idea, if I'm doing it wrong or something. thanks for help.
You have a major problem, you are continually removing items from the array while looping through it. You have to go back one step (i--) every time an item is removed.
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (!isNaN(arr[i])) { // isNaN return true if it's not a valid number, so we have to inverse the test
arr.splice(i, 1); // if it's a valid number remove the element at the index i (no need to search for the index using indexOf, we already have it its i)
i--; // if we remove an element, we have to go back one step (or we will leave one item behind every time we remove another)
}
}
console.log(arr);
You could use typeof instead:
var arr = [1,2,3,'some','string'];
for (var i = 0; i < arr.length; i++) {
if (typeof arr[i] == 'number') {
arr.splice(arr.indexOf(arr[i], 1));
}
}
The `.splice()' method makes changes to the array itself.
So for each iteration of the array, you are changing it fundamentally.
If you want the array to include only integers:
var arr = [1,2,3,'some','string'];
var newArray = [];
arr.forEach(function(element) {
if (Number.isInteger(element)){
newArray.push(element);
}
});
console.log(newArray);
I'd like to pull a particular object from an array of objects based on a unique property of that object (ie, a key).
In the following, I'm searching for an element in 'arr' where the key is 8.
var myElement = arr.filter(function(element) {
return element.key === 8;
});
This works, but every time this runs, it will iterate through all elements in the array, even after the correct element has been found. For example, if it finds myElement at index 4, but there are 100 elements in the array, the snippet is running 25x more than it needs to.
Is there a more efficient way, or a way to terminate filter() when it has found myElement?
I feel like I've missed something obvious...
You actually want a find function that returns when the first occurrence is found. You can use Array.prototype.find if you are on ECMAScript 6 spec or you can implement a simple find like:
function find8(arr) {
// type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
if (arr[i].key === 8) {
return arr[i];
}
}
throw new Error('Not found');
}
If you want a more generic solution that accepts predicates (i.e. functions that return a boolean value), you can write a more generic function like:
function find(arr, predicate) {
// Again, type checks skipped for brevity
var i = 0,
j = arr.length;
for (; i < j; i++) {
// Note the condition is now a call to the predicate function
if (predicate(arr[i])) {
return arr[i];
}
}
throw new Error('Not found');
}
for (var i=0; i<arr.length; i++) {
if (arr[i].key === 8) {
console.log('FOUND ITEM AT KEY '+8);
break;
}
}
It sounds like you'd be better off with something like this:
function findByKey(items, key) {
for (var index = 0; index < items.length; index++) {
var item = items[index];
if (item.key === key) {
return item;
}
}
return null;
}
Note that if your list is sorted, you can improve upon that by doing a binary-search instead. Though personally I'd suggest hashing (assuming that the same key isn't used twice). Something like:
var array = [/* your data here... */];
var map = {};
//do this once; or once each time your array changes
for (var index = 0; index < array.length; index++) {
var item = array[index];
map[item.key] = item;
}
//now find things by doing this
var itemWithKey8 = map[8];
I'd handle this with a simple for loop (used a function for generic re-use):
function findObject(arr, cond)
for (i = 0; i < arr.length; i++) {
if (cond(arr[i]){
return arr[i];
}
}
}
// now call fincObject(myArray, function(element){element.key === 8})
Or, if you know you are going to do this many times, create a mapping which should be a lot faster:
function makeMapping(arr, keyFunc){
var mapping = {};
for (var i = 0; i < arr.length; i++) {
mapping[keyFunc(arr[i])] = arr[i];
}
return mapping;
}
This returns an object mapping 8 to the object with key.id === 8. Your keyFunc would be:
function keyFunc(element){
return element.key;
}
Why reinvent the wheel? There are a lot of similar answers here, so I'll share a different approach--by far my favorite approach. There is an excellent library called linq.js (both standalone and jQuery plugin versions) which makes searching, filtering, sorting, etc. a breeze.
var myElement = Enumerable.From(arr).FirstOrDefault(null, function(element) {
return element.key === 8;
});
In the above example, the first element that matches the conditions is returned. If nothing is found, null is returned (the first parameter is the default value to return).