Find maximum occurance of string element in an array in javascript - javascript

I have an Array of string elements and I need to find how many times a elements occured in a Array.
my Array is following:
var x=["water","water","water","land", "land","land","land","forest"];
I need to know which element is prominent in Array. I have tried to use example from this discussion "Counting the occurrences of JavaScript array elements".
But I did not get any expected result.
Please help me to find a possible solution. :-)

There may not be an unambiguous answer to which item occurs the most times. Here is how you can get the item counts in a functional style:
x.reduce(function(counts, key) {
if(!counts.hasOwnProperty(key))
counts[key] = 0
counts[key] = counts[key] + 1
return counts }, {})
Returns {"water": 3, "land": 4, "forest": 1}

Related

Intersect multiple arrays of objects

So first of all, I am not expecting a specific solution to my problem, but instead some insights from more experienced developers that could enlighten me and put me on the right track. As I am not yet experienced enough in algorithms and data structures and I take this as a challenge for myself.
I have n number of arrays, where n >= 2.
They all contain objects and in the end, I want an array that contains only the common elements between all these arrays.
array1 = [{ id: 1 }, { id: 2 }, { id: 6 }, { id: 10 }]
array2 = [{ id: 2 }, { id: 4 }, { id: 10 }]
array3 = [{ id: 2 }, { id: 3 }, { id: 10 }]
arrayOfArrays = [array1, array2, array3]
intersect = [{ id: 2 }, { id: 10 }]
How would one approach this problem? I have read solutions using Divide And Conquer, or Hash tables, and even using the lodash library but I would like to implement my own solution for once and not rely on anything external, and at the same time practice algorithms.
For efficiency, I would start by locating the shortest array. This should be the one you work with. You can run a reduce on the arrayOfArrays to iterate through and return the index of the shortest length.
const shortestIndex = arrayOfArrays.reduce((accumulator, currentArray, currentIndex) => currentArray.length < arrayOfArrays[index] ? currentIndex : accumulator, 0);
Take the shortest array and call the reduce function again, this will iterate through the array and allow you to accumulate a final value. The second parameter is the starting value, which is a new array.
shortestArray.reduce((accumulator, currentObject) => /*TODO*/, [])
For the code, we basically need to loop through the remaining arrays and make sure it exists in all of them. You can use the every function since it will fail fast meaning the first array it doesn't exist in will trigger it to return false.
Inside the every you can call some to check if there is at least one match.
isMatch = remainingArrays.every(array => array.some(object => object.id === currentObject.id))
If it's a match, add it to the accumulator which will be your final result. Otherwise, just return the accumulator.
return isMatch ? [...accumulator, currentObject] : accumulator;
Putting all that together should get you a decent solution. I'm sure there are more optimizations that could be made, but that's where I would start.
reduce
every
some
The general solution is to iterate through an input and check for each value whether it exists in all of the other inputs. (Time complexity: O(l * n * l) where n is number of arrays and l is the average length of an array)
Following the ideas of the other two answers, we can improve this brute-force approach a bit by
iterating through the smallest input
using a Set for efficient lookup of ids instead of iteration
so it becomes (with O(l * n + min_l * n) = O(n * l))
const arrayOfIdSets = arrayOfArrays.map(arr =>
new Set(arr.map(val => val.id))
);
const smallestArray = arrayOfArrays.reduce((smallest, arr) =>
smallest.length < arr.length ? smallest : arr
);
const intersection = smallestArray.filter(val =>
arrayOfIdSets.every(set => set.has(val.id))
);
A good way to approach these kinds of problems, both in interviews and in just regular life, is to think of the most obvious approach you can come up with, no matter how inefficient, and think think about how you can improve it. This is usually called a "brute force" approach.
So for this problem, perhaps an obvious but inefficient approach would be to iterate through every item in array1 and check if it is in both array2 and array 3, and note it down (in another array) if it is. Then repeat again for each item in array2 and in array 3, making sure to only note down items you haven't noted down before.
We can see that will be inefficient because we'll be looking for a single item in an array many times, which is quite slow for an array. But it'll work!
Now we can get to work improving our solution. One thing to notice is that finding the intersection of 3 arrays is the same as finding the intersection of the third array with the intersection of the first and second array. So we can look for a solution to the simpler problem of the intersection of 2 arrays, to build one of an intersection for 3 arrays.
This is where it's handy to know your datastructures. You want to be able to ask the question, "does this structure contain a particular element?" as quickly as possible. Think about what structures are good for that kind of a lookup (known as search). More experienced engineers have this memorized/learned, but you can reference something like https://www.bigocheatsheet.com/ to see that sets are good at this.
I'll stop there to not give the full solution, but once you've seen that sets are fast at both insertion and search, think about how you can use that to solve your problem.

Understanding indexOf and lastIndexOf

I'am doing some JavaScript exercises and I stumbled upon this one "Write a JavaScript program to filter out the non-unique values in an array."
I tried and found a solution, which worked but it was to cumbersome. A better answer, according to the site, is the following:
const filter_Non_Unique = arr =>
arr.filter(l => arr.indexOf(l) === arr.lastIndexOf(l));
console.log(filter_Non_Unique([1,2,3,4,4,5,6,6])) // 1,2,3,5
Now I recked my head trying to understand why this solution works but I still don't get it.
Can somebody explain to me?
Thanks in advance.
If the element only occurs once in the array, the first index will be the same as the last index, the index will not change for both calls.
eg:
const arr = [1,2,3,4,4,5,6,6]
console.log(arr.indexOf(5))
console.log(arr.lastIndexOf(5))
Since both of of these functions return the same index, filter will keep the element in.
On the other hand if there are multiple values, those values will have different indexes, so the filter will return false, and remove it from the array:
const arr = [1,2,3,4,4,5,6,6]
console.log(arr.indexOf(4))
console.log(arr.lastIndexOf(4))
I've answered a question similar to the one you solved here, you could try that logic too.
Beside of the indexOf/lastIndexOf approach which needs a lot of iterating the array, you could take a two loop approach.
By getting an array of single items by using a hash table and three states, like
undefined, the standard value of not declared properties of an object,
true for the first found value,
false for all values who are repeated in the array.
Then filter by the value of the hash table.
const
filterNonUnique = array => {
var hash = {};
for (let v of array) hash[v] = hash[v] === undefined;
return array.filter(v => hash[v]);
}
console.log(filterNonUnique([1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 7]))

How can I compensate for a removed element in an array iteration that is still in progress? [duplicate]

This question already has answers here:
Filter and delete filtered elements in an array
(10 answers)
Closed 3 years ago.
I am iterating over an array of strings using forEach(), and testing each element's length to determine wether it is even or odd. If the length of the string is even, it will be removed using splice().
My input and output are shown below, and as you can see even though (i think) my conditions are correct, in my return array, I still get an even, two character word - which should have been spliced out.
Code:
function filterOddLengthWords(words) {
words.forEach(function(element, index) {
if (element.length%2===0) {
words.splice(index, 1);
}
})
return words;
}
var output = filterOddLengthWords(['there', 'it', 'is', 'now']);
console.log(output); // --> [ 'there', 'is', 'now' ]
I understand where the error is, but I just don't know how to compensate for it. I could possibly rewrite this by creating an empty array at the beginning of the function, and then testing each element against the inverse condition, using the push() method to add each positive to the empty array. However, that is more inefficient and I'm curious to see if my way is possible. Thanks for the answers in advance.
By splicing an array, you change the index, but forEach takes the elements in advance and the old indices.
Usually by using splice, the iteration is started from the end and if some item is remoce, the index remains for the items before.
You could filter the array.
function filterOddLengthWords(words) {
return words.filter(function(element) {
return element.length % 2;
});
}
var output = filterOddLengthWords(['there', 'it', 'is', 'now']);
console.log(output);
The problem with splicing the array in the forEach() is that when you splice() on the array at index 1 which has the element it the element at index 2 is is moved to index 1.
So in the next iteration when the index is 2 in the callback the element at the index 2 of the array is the value now instead of the element is. As now is odd it is kept but the element is is completely skipped.
Using Array.prototype.filter will work here as it does not modify the original array but instead collects the valid results into a new array.

Do undefined elements in an array have an impact in JS? [duplicate]

This question already has answers here:
Deleting array elements in JavaScript - delete vs splice
(29 answers)
Closed 4 years ago.
I couldn't find a question that specifically targets the issue I'm having hence this question is being asked.
I have an array that holds 5 numbers:
var numbers = [0,1,2,3,4];
Once a number is clicked on the frontend (website), the number is removed from the array using the below code:
delete numbers[1];
This removes the correct number but leaves a space where the number was (the space is undefined). I believe this is causing an issue. After a number is removed from the array, I use another function to randomly pick any of the remaining numbers in the array however it sometimes fails. After much thought, I've realized it may be because there are empty spaces in the array after a number is removed and as a result, the code fails to work due to the undefined element.
Is my analogy correct or am I mistaken?
(I have also attempted to use the splice method to remove the number however that then causes an issue with the length of my array because if I later want to remove another number, it removes the wrong one due to the numbers moving around etc).
What you'd want to use is splice
In your specific case, numbers.splice(1,1)
You're correct that delete replaces one of the values in the array with undefined, and does not change the array length. Later on when you randomly choose an element from the array, you can wind up getting that undefined value, because it's still taking up a slot in the array:
var numbers = [0,1,2,3,4];
delete numbers[3];
console.log(numbers)
Instead use splice, which removes the item from the array completely:
var numbers = [0,1,2,3,4];
numbers.splice(3,1) /// remove one element starting at index 3
console.log(numbers)
if I later want to remove another number, it removes the wrong one due to the numbers moving around
You do need to choose one behavior or the other. If you need to preserve indexes as is, then continue to use delete, leaving the undefined values in the array, and rewrite your "choose one at random" function to never pick undefined values:
// start with some undefined values:
var numbers = [0, 1, undefined, undefined, undefined, 5]
var pickRandom = function(numbers) {
// make a copy of the array, removing undefined elements:
var definedValues = numbers.filter(function(item) {
return item !== undefined;
});
if (definedValues.length === 0) {return false}
//choose one at random:
return definedValues[Math.floor(Math.random() * definedValues.length)]
}
// test it:
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
console.log(pickRandom(numbers));
(...but note that this suggests that a simple array is the wrong data structure to use here; you may be better off with an array of objects each with an explicit ID, so you can reference specific ones as needed without worrying about keeping the array index the same.)
If you mean to actually remove the element from the array, leaving your array with 4 elements, then you can use
numbers.splice(1);
This will remove the element in the index 1 from the array and return the section of the new array.

using .slice method on an array

I'm practicing the array section of JavaScript Koan and I'm not fully understanding why these answers are correct. I added my assumptions below if someone could please clarify/let me know if I'm wrong :
it("should slice arrays", function () {
var array = ["peanut", "butter", "and", "jelly"];
expect(array.slice(3, 0)).toEqual([]);
Why wouldn't it at least slice "jelly" since the slice begins with
3? How does the cut off of 0 make it empty instead?
expect(array.slice(3, 100)).toEqual(["jelly"]);
If the cut off index goes beyond what currently exists in the array,
does this mean that a new array created from slice would contain all
indexes starting at 3 until the end of the array?
expect(array.slice(5, 1)).toEqual([undefined];
Will it always be undefined if the starting index doesn't exist in the
array?
});
The second argument to Array.slice() is the upper bound of the slice.
Think of it as array.slice(lowestIndex, highestIndex).
When you slice from index 3 to index 100, there is one item (in your case) that has index >= 3 and < 100, so you get an array with that one item. When you try to take a slice from index 3 to index 0, there can't be any items that meet the conditions index >= 3 and < 0, so you get an empty array.
--EDIT--
Also, array.slice() should never return undefined. That's one of the advantages of using it. If there are no matching values in the array, you just get back an empty array. Even if you say var a = new Array() and don't add any values to it, calling a.slice(0,1) will just give you an empty array back. Slicing from outside of the array bounds will just return an empty array also. a.slice(250) will return [] whereas a[250] will be undefined.

Categories

Resources