Create object with total value from associated string array and integer array - javascript

I have a string array of product IDs, like this:
["A", "A", "B", "A"]
And another integer array of prices, like this:
[30, 50, 10, 40]
What would be the best way to produce a Javascript object with the unique item and its total cost, as the order of the integers are the prices associated with the same order of product numbers, so ideally it would return an object like this i.e.
{"A": 120, "B": 10}
Thank you!
I am relatively new to Javascript and SQL but I have tried using a foreach statement which I used successfully to produce a unique count of the item when I extract just that column into an array but not the problem as described above.

Simple reduce loop
const productIds = ["A", "A", "B", "A"];
const prices = [30, 50, 10, 40]
const totals = productIds.reduce((acc, key, index) => {
acc[key] = (acc[key] || 0) + prices[index];
return acc;
}, {});
console.log(totals);

Assuming that both the arrays are of equal lengths, by using a linear search with a map to store and add the count would be the correct approach in this case:
function getSummation(productIds, prices) {
const store = {};
for (let i=0; i < productIds.length; i++) {
if (!store[productIds[i]])
store[productIds[i]] = 0;
store[productIds[i]] += prices[i];
}
return store;
}

Shorter version using comma operator
const products = ["A", "A", "B", "A"];
const prices = [30, 50, 10, 40];
const obj = products
.reduce((acc,cur,i) => (acc[cur] = (acc[cur] ??= 0) + prices[i], acc),{});
console.log(obj)

const keys = ["A", "A", "B", "A"];
const values = [30, 50, 10, 40];
const result = keys.reduce((t, v, i) => ({...t, [v]: (t[v] || 0) + values[i]}), {});
console.log(result);

Related

Google Apps Script: Find the location of every item in array

I would like to run through every item in a 1D array and perform an indexOf on another 1D array to determine where it matches.
If I have:
Array 1 = ["a","d","e"]
Array 2 = ["a","b","c","d","e","f"]
I would like to transform Array 1 from values into match locations where it would become: [0,3,4].
I tried the equation below, but it didn't work. I thought using forEach() I could go through every item in Array 1. Then I could run a function to perform an indexOf() on Array 2 for each item. What am I doing wrong??
var array1 = ["a","d","e"];
var array2 = ["a","b","c","d","e","f"];
var array1count = array1.forEach( function (e) { array2.indexOf( e )});
Logger.log(array1count)
If array1 has the same order as array2, you could take a closure over the index and use a while loop inside of the callback for mapping found indices.
var array1 = ["a", "d", "e"],
array2 = ["a", "b", "c", "d", "e", "f"],
indices = array1.map((i => v => {
while (array2[++i] !== v) ;
return i;
})(-1));
console.log(indices); // [0, 3, 4]
For not defined order, you neeeither a single loop for gathering the indices and another to map the collected indices.
var array1 = ["a", "d", "e"],
array2 = ["a", "b", "c", "d", "e", "f"],
indices = array1.map(
Map.prototype.get,
array2.reduce((m, v, i) => m.set(v, i), new Map)
);
console.log(indices); // [0, 3, 4]
forEach() returns undefined, you can try using Array.prototype.reduce():
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
var array1 = ["a","d","e"];
var array2 = ["a","b","c","d","e","f"];
var array1count = array2.reduce(function(acc, curr, i) {
if(array1.includes(curr)) //check if the current item is present in array1
acc.push(i);
return acc;
},[]);
console.log(array1count);
I ran this equation and it worked. Seems to be pretty simple and straightforward. Hopefully I'm not missing something here that the other answers are resolving.
var array1 = ["a","d","e"];
var array2 = ["a","b","c","d","e","f"];
var array1count = array1.map( function (item) { return array2.indexOf(item) });
Logger.log(array1count)

Common elements in multiple arrays in angular 6 [duplicate]

I am aware of this question, simplest code for array intersection but all the solutions presume the number of arrays is two, which cannot be certain in my case.
I have divs on a page with data that contains arrays. I want to find the values common to all arrays. I do not know how many divs/arrays I will have in advance. What is the best way to calculate values common to all arrays?
var array1 = ["Lorem", "ipsum", "dolor"];
var array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
var array3 = ["Jumps", "Over", "Lazy", "Lorem"];
var array4 = [1337, 420, 666, "Lorem"];
//Result should be ["Lorem"];
I found another solution elsewhere, using Underscore.js.
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
_.intersection.apply(_, arrayOfArrays)
//Result is [43]
I've tested this with simple dummy data at my end and it seems to work. But for some reason, some of the arrays I'm producing, which contain simple strings, also automatically include an added value, "equals: function":
["Dummy1", "Dummy2", "Dummy3", equals: function]
And whenever I use the Underscore.js intersection method, on an array of arrays, I always get [equals: function] in dev tools, and not - if "Dummy3" is common to all arrays - ["Dummy3"].
So TL;DR is there another solution to array intersection that would suit my case? And can anyone explain what [equals: function] means here? When I expand the item in the dev tools, it produces an empty array and a list of methods available on arrays (pop, push, shift etc), but these methods are all faded out, while equals: function is highlighted.
You could just use Array#reduce with Array#filter and Array#includes.
var array1 = ["Lorem", "ipsum", "dolor"],
array2 = ["Lorem", "ipsum", "quick", "brown", "foo"],
array3 = ["Jumps", "Over", "Lazy", "Lorem"],
array4 = [1337, 420, 666, "Lorem"],
data = [array1, array2, array3, array4],
result = data.reduce((a, b) => a.filter(c => b.includes(c)));
console.log(result);
I wrote a helper function for this:
function intersection() {
var result = [];
var lists;
if(arguments.length === 1) {
lists = arguments[0];
} else {
lists = arguments;
}
for(var i = 0; i < lists.length; i++) {
var currentList = lists[i];
for(var y = 0; y < currentList.length; y++) {
var currentValue = currentList[y];
if(result.indexOf(currentValue) === -1) {
var existsInAll = true;
for(var x = 0; x < lists.length; x++) {
if(lists[x].indexOf(currentValue) === -1) {
existsInAll = false;
break;
}
}
if(existsInAll) {
result.push(currentValue);
}
}
}
}
return result;
}
Use it like this:
intersection(array1, array2, array3, array4); //["Lorem"]
Or like this:
intersection([array1, array2, array3, array4]); //["Lorem"]
Full code here
UPDATE 1
A slightly smaller implementation here using filter
This can be done pretty succinctly if you fancy employing some recursion and the new ES2015 syntax:
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
const arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
// Filter xs where, for a given x, there exists some y in ys where y === x.
const intersect2 = (xs,ys) => xs.filter(x => ys.some(y => y === x));
// When there is only one array left, return it (the termination condition
// of the recursion). Otherwise first find the intersection of the first
// two arrays (intersect2), then repeat the whole process for that result
// combined with the remaining arrays (intersect). Thus the number of arrays
// passed as arguments to intersect is reduced by one each time, until
// there is only one array remaining.
const intersect = (xs,ys,...rest) => ys === undefined ? xs : intersect(intersect2(xs,ys),...rest);
console.log(intersect(array1, array2, array3, array4));
console.log(intersect(...arrayOfArrays));
// Alternatively, in old money,
var intersect2ES5 = function (xs, ys) {
return xs.filter(function (x) {
return ys.some(function (y) {
return y === x;
});
});
};
// Changed slightly from above, to take a single array of arrays,
// which matches the underscore.js approach in the Q., and is better anyhow.
var intersectES5 = function (zss) {
var xs = zss[0];
var ys = zss[1];
var rest = zss.slice(2);
if (ys === undefined) {
return xs;
}
return intersectES5([intersect2ES5(xs, ys)].concat(rest));
};
console.log(intersectES5([array1, array2, array3, array4]));
console.log(intersectES5(arrayOfArrays));
Using a combination of ideas from several contributors and the latest ES6 goodness, I arrived at
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
Array.prototype.intersect = function intersect(a, ...b) {
const c = function (a, b) {
b = new Set(b);
return a.filter((a) => b.has(a));
};
return undefined === a ? this : intersect.call(c(this, a), ...b);
};
console.log(array1.intersect(array2, array3, array4));
// ["Lorem"]
For anyone confused by this in the future,
_.intersection.apply(_, arrayOfArrays)
Is in fact the most elegant way to do this. But:
var arrayOfArrays = [[43, 34343, 23232], [43, 314159, 343], [43, 243]];
arrayOfArrays = _.intersection.apply(_, arrayOfArrays);
Will not work! Must do
var differentVariableName = _.intersection.apply(_,arrayOfArrays);
The cleanest way I've found to do this wasn't actually listed on this page, so here you are:
arrays[0].filter(elem => arrays.every(array => array.includes(elem)))
Reads like nice, clear english: every array includes the element. It assumes that you have at least 1 element in arrays, though. If you can't make this assumption, you can use optional chaining:
arrays?[0].filter(elem => arrays.every(array => array.includes(elem))) ?? []
Your code with _lodash is working fine.
As you can say in this fiddle:
this code:
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
var a = _.intersection.apply(_, arrayOfArrays);
console.log(a);
console.log(a.length);
Will have output:
[42]
1
Maybe you see
equals: function
because you are using kind of debugger.
Try to just print the array with console.log, you will get only 42.
Small recursive divide and conquer solution that does not rely on es6 or any library.
It accepts an array of arrays which makes the code shorter and allows you to pass arguments by using map.
function intersection(a) {
if (a.length > 2)
return intersection([intersection(a.slice(0, a.length / 2)), intersection(a.slice(a.length / 2))]);
if (a.length == 1)
return a[0];
return a[0].filter(function(item) {
return a[1].indexOf(item) !== -1;
});
}
var list1 = [ 'a', 'b', 'c' ];
var list2 = [ 'd', 'b', 'e' ];
var list3 = [ 'f', 'b', 'e' ];
console.log(intersection([list1, list2, list3]));
If you can use ES6 Maps and your arrays items are scalar values (easily usable as Map keys), then you can try this (works in my case) :
const intersect_lists = (lists) => {
const results = []
const lookup = new Map()
lists.map((list, idx) => {
list.map((element) => {
const count = lookup.get(element) || 0
if(count === idx) {
lookup.set(element, 1 + count)
} else {
lookup.delete(element)
}
})
})
// only elements present in all lists will have
// their respective counter equllling the total number of lists
Array.from(lookup.keys()).map((key) => {
if(lookup.get(key) === lists.length) {
results.push(key)
}
})
return results
}
Optionally you can pre-sort "lists" (of lists) by creasing length to avoid lots of iterations of the outer map() call, especially if lists lengths are heterogenous :
lists.sort((l1, l2) => l1.length - l2.length).map((list, idx) => { ... })
Sol with Maps
// nums1 = [1,2,2,1], nums2 = [2,2]
// n m
// O(nm) + space O(min(n, m))
// preprocess nums2 to a Map<number, count>
// O(n + m) + space(min(n, m))
// process the shorter one
let preprocessTarget = nums1
let loopTarget = nums2
if (nums1.length > nums2.length) {
preprocessTarget = nums2
loopTarget = nums1
}
// Map<element, number>
const countMap = new Map()
for (let num of preprocessTarget) {
if (countMap.has(num)) {
countMap.set(num, countMap.get(num) + 1)
} else {
countMap.set(num, 1)
}
}
const result = []
for (let num of loopTarget) {
if (countMap.has(num)) {
result.push(num)
const count = countMap.get(num)
if (count === 1) {
countMap.delete(num)
} else {
countMap.set(num, count - 1)
}
}
}
return result
const data = [array1, array2, array3, array4].filter(arr => arr.length > 0);
const result = [...new Set(data)];
let final = Array.of<any>();
for (const key of result) {
final = final.concat(key);
}
console.log(final);
Lodash pure:
_.keys(_.pickBy(_.groupBy(_.flatten(arrays)), function (e) {return e.length > 1}))
Lodash with plain js:
var elements = {}, duplicates = {};
_.each(arrays, function (array) {
_.each(array, function (element) {
if (!elements[element]) {
elements[element] = true;
} else {
duplicates[element] = true;
}
});
});
_.keys(duplicates);
I manage to accomplish this with a reduce call:
var intersected = intersect([[1, 2, 3], [2, 3, 4], [3, 4, 5]]);
console.log(intersected); // [3]
function intersect(arrays) {
if (0 === arrays.length) {
return [];
}
return arrays.reduce((intersection, array) => {
return intersection.filter(intersectedItem => array.some(item => intersectedItem === item));
}, arrays[0]);
}
Intersection of a variable number of arrays.
This is how I do it:
function getArraysIntersection(list1, list2, ...otherLists) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (otherLists.length) {
return getArraysIntersection(result, otherLists.shift(), ...otherLists);
}
else {
return result;
}
}
SNIPPET
function getArraysIntersection(list1, list2, ...otherLists) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (otherLists.length) {
return getArraysIntersection(result, otherLists.shift(), ...otherLists);
}
else {
return result;
}
}
const a = {label: "a", value: "value_A"};
const b = {label: "b", value: "value_B"};
const c = {label: "c", value: "value_C"};
const d = {label: "d", value: "value_D"};
const e = {label: "e", value: "value_E"};
const arr1 = [a,b,c];
const arr2 = [a,b,c];
const arr3 = [c];
const t0 = performance.now();
const intersection = getArraysIntersection(arr1,arr2,arr3);
const t1 = performance.now();
console.log('This took t1-t0: ' + (t1-t0).toFixed(2) + ' ms');
console.log(intersection);
const intersect = (arrayA, arrayB) => {
return arrayA.filter(elem => arrayB.includes(elem));
};
const intersectAll = (...arrays) => {
if (!Array.isArray(arrays) || arrays.length === 0) return [];
if (arrays.length === 1) return arrays[0];
return intersectAll(intersect(arrays[0], arrays[1]), ...arrays.slice(2));
};
For anyone who might need, this implements the intersection inside an array of arrays:
intersection(array) {
if (array.length === 1)
return array[0];
else {
array[1] = array[0].filter(value => array[1].includes(value));
array.shift();
return intersection(array);
}
}
function getIntersection(ar1,ar2,...arrays){
if(!ar2) return ar1
let intersection = ar1.filter(value => ar2.includes(value));
if(arrays.length ===0 ) return intersection
return getIntersection(intersection,...arrays)
}
console.log(getIntersection([1,2,3], [3,4], [5,6,3]) // [3]
Function to calculate intersection of multiple arrays in JavaScript
Write a method that creates an array of unique values that are included in all given arrays. Expected Result: ([1, 2], [2, 3]) => [2]
const arr1 = [1, 2, 1, 2, 1, 2];
const arr2 = [2, 3];
const arr3 = ["a", "b"];
const arr4 = ["b", "c"];
const arr5 = ["b", "e", "c"];
const arr6 = ["b", "b", "e"];
const arr7 = ["b", "c", "e"];
const arr8 = ["b", "e", "c"];
const intersection = (...arrays) => {
(data = [...arrays]),
(result = data.reduce((a, b) => a.filter((c) => b.includes(c))));
return [...new Set(result)];
};
console.log(intersection(arr1, arr2)); // [2]
console.log(intersection(arr3, arr4, arr5)); // ['b']
console.log(intersection(arr5, arr6, arr7, arr8)); // ['b', 'e']

How to implement logic if sum of array is 0,remove from array in JavaScript?

I have array of array like below.
[["a",0,0,0],["b",30,20,10],["c",40,50,60],["d",0,0,0],...]
I want to remove some array if sum of number inside of each array is zero and get following return.
[["b",30,20,10],["c",40,50,60],...]
Could anyone tell how I can implement logic to achieve this in JavaScript?
filter the array of arrays, by iterating over each array and keeping track of the sum of the numbers in the array with reduce:
const input = [
["a", 0, 0, 0],
["b", 30, 20, 10],
["c", 40, 50, 60],
["d", 0, 0, 0],
['e', 30, -30]
];
console.log(
input.filter((arr) => (
arr.reduce((a, item) => (
typeof item === 'number'
? a + item
: a
), 0)
))
);
Use filter method and return only those nestedarray where the sum is not 0
let x = [
["a", 0, 0, 0],
["b", 30, 20, 10],
["c", 40, 50, 60],
["d", 0, 0, 0]
];
let result = x.filter(function(item) {
// a variable to hold the sum
let sum = 0;
// since the first value of inner array is string
// start looping from index 1
for (var i = 1; i < item.length; i++) {
sum += item[i];
}
// return the nested array only if the sum is not 0
if (sum !== 0) {
return item;
}
})
console.log(result)
Lots of ways to do this. For me, it's easy to split the problem up into two parts.
Find out if the numbers add up to 0
Remove arrays that have a sum of 0
Study up on the reduce and filter methods if you need to.
// Determine if a subarray adds up to 0.
function sumIsZero(arr) {
// You could also do
// const rest = arr.slice(1)
// If you're not familiar with rest spread operators
const [letter, ...rest] = arr;
return rest.reduce((x, y) => x + y, 0) === 0;
}
// Create a new array that only includes the subarrays that add up to 0
const filtered = arrs.filter(sumIsZero);
Use filter and reduce to count sums of numbers:
const a = [
["a", 0, 0, 0],
["b", 30, 20, 10],
["c", 40, 50, 60],
["d", 0, 0, 0]
]
var filtered = a.filter(e => !!e.reduce((a, n) => isNaN(n) ? a : a+n, 0))
console.log(JSON.stringify(filtered))
Another approach:
const a = [
["a", 0, 0, 0],
["b", 30, 20, 10],
["c", 40, 50, 60],
["d", 0, 0, 0]
]
var f = []
while (e = a.pop())
if(e.reduce((a, n) => isNaN(n) ? a : a+n, 0)) f.push(e)
console.log(JSON.stringify(f))
This is the dumb method:
const array = [["a",0,0,0],["b",30,20,10],["c",40,50,60],["d",0,0,0]]
var filteredArray = []
for (const subArray of array) {
var subArrayJustNumbers = subArray.slice(0) // copy the subArray
subArrayJustNumbers.shift() // remove the the first object
var sum = 0 // add all the numbers in the subarray
for (const n of subArrayJustNumbers)
sum += n
if (sum != 0) { // if the sum of all numbers isn't zero, append it to the filteredArray
filteredArray.push(subArray)
}
}

How to get value from an array, only those what is greater than all elements to its right

I wanted to write a function to get all numbers, what is greater than all elements to its right.
Example if i have an array like this:
arr = [ 75,47,42,56,13,55];
I want a result like this [75,56,55]in a new array.
Other example if i have an array like this:
arr = [16,17,14,3,14,5,2]
I want a result like: [17,14,5,2]
What methods can i use to get this result whatever numbers i have in an array?
You can use filter the array. splice the array to get all the numbers on the right. Use every to check if all array elements are greater than the value.
let arr = [75, 47, 42, 56, 13, 55];
let result = arr.filter((v, i, a) => [...a].splice(i + 1, a.length).every(o => v > o));
console.log(result);
Doc: filter(), splice(), every()
You could simply iterate from the right side and check against the latest found greatest value.
function greaterThanRight(array) {
return array.reduceRight((r, v) => [].concat(v <= r[0] ? [] : v, r), [])
}
console.log([[75, 47, 42, 56, 13, 55], [16, 17, 14, 3, 14, 5, 2]].map(greaterThanRight).map(a => a.join(' ')));
A simple for loop where inner loop has index starting with the index value of the match having highest right value:
var arr = [ 75,47,42,56,13,55];
var res = [];
for(var i=0; i<arr.length; i++){
var highestValue = arr[i];
for(var j=i+1; j<arr.length; j++){
if(highestValue < arr[j]){
highestValue = arr[j];
i = j;
break;
}
}
res.push(highestValue);
}
console.log(res);
var arr = [75,47,42,56,13,55];
arr.sort(function(a, b) {
// a = current item in array
// b = next item in array
return b - a;
});
var newArr = arr.slice(0,3);
console.log(newArr);
Try this. Just start checking from the right.keep a variable max and update it whenever you found a new max
var arr = [ 75,47,42,56,13,55];
function findMaxRight(arr){
var res=[]
var max = Number.MIN_SAFE_INTEGER
for(var i=arr.length -1; i>=0;i--){
if(arr[i]> max){
res.unshift(arr[i])
max = arr[i]
}
}
return res
}
console.log(findMaxRight(arr));
One way to do it could be to loop your array, get the current item and get the rest of the array after the current item using slice.
Then sort the rest of the array descending and get the first entry which is the highest one.
If the current value is larger than the highest one, it is larger then all the values on the right:
let items = [75, 47, 42, 56, 13, 55];
let result = [];
items.forEach((item, index) => {
let head = items[index];
let tail = items.slice(index + 1).sort(function(a, b) {
return b - a;
});
if ((head > tail[0]) || (index === items.length - 1)) {
result.push(head);
}
});
console.log(result);
My solution to do this : find the max and if it's the current number store it, continue with the new array looping = looping.slice(1)
let arr = [75,47,42,56,13,55];
let looping = [...arr];
let finish = arr.reduce( (acc, x) => {
if(x === Math.max(...looping)) acc.push(x);
looping = looping.slice(1);
return acc;
}, [])
console.log(finish) // [75,56,55]

How to calculate intersection of multiple arrays in JavaScript? And what does [equals: function] mean?

I am aware of this question, simplest code for array intersection but all the solutions presume the number of arrays is two, which cannot be certain in my case.
I have divs on a page with data that contains arrays. I want to find the values common to all arrays. I do not know how many divs/arrays I will have in advance. What is the best way to calculate values common to all arrays?
var array1 = ["Lorem", "ipsum", "dolor"];
var array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
var array3 = ["Jumps", "Over", "Lazy", "Lorem"];
var array4 = [1337, 420, 666, "Lorem"];
//Result should be ["Lorem"];
I found another solution elsewhere, using Underscore.js.
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
_.intersection.apply(_, arrayOfArrays)
//Result is [43]
I've tested this with simple dummy data at my end and it seems to work. But for some reason, some of the arrays I'm producing, which contain simple strings, also automatically include an added value, "equals: function":
["Dummy1", "Dummy2", "Dummy3", equals: function]
And whenever I use the Underscore.js intersection method, on an array of arrays, I always get [equals: function] in dev tools, and not - if "Dummy3" is common to all arrays - ["Dummy3"].
So TL;DR is there another solution to array intersection that would suit my case? And can anyone explain what [equals: function] means here? When I expand the item in the dev tools, it produces an empty array and a list of methods available on arrays (pop, push, shift etc), but these methods are all faded out, while equals: function is highlighted.
You could just use Array#reduce with Array#filter and Array#includes.
var array1 = ["Lorem", "ipsum", "dolor"],
array2 = ["Lorem", "ipsum", "quick", "brown", "foo"],
array3 = ["Jumps", "Over", "Lazy", "Lorem"],
array4 = [1337, 420, 666, "Lorem"],
data = [array1, array2, array3, array4],
result = data.reduce((a, b) => a.filter(c => b.includes(c)));
console.log(result);
I wrote a helper function for this:
function intersection() {
var result = [];
var lists;
if(arguments.length === 1) {
lists = arguments[0];
} else {
lists = arguments;
}
for(var i = 0; i < lists.length; i++) {
var currentList = lists[i];
for(var y = 0; y < currentList.length; y++) {
var currentValue = currentList[y];
if(result.indexOf(currentValue) === -1) {
var existsInAll = true;
for(var x = 0; x < lists.length; x++) {
if(lists[x].indexOf(currentValue) === -1) {
existsInAll = false;
break;
}
}
if(existsInAll) {
result.push(currentValue);
}
}
}
}
return result;
}
Use it like this:
intersection(array1, array2, array3, array4); //["Lorem"]
Or like this:
intersection([array1, array2, array3, array4]); //["Lorem"]
Full code here
UPDATE 1
A slightly smaller implementation here using filter
This can be done pretty succinctly if you fancy employing some recursion and the new ES2015 syntax:
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
const arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
// Filter xs where, for a given x, there exists some y in ys where y === x.
const intersect2 = (xs,ys) => xs.filter(x => ys.some(y => y === x));
// When there is only one array left, return it (the termination condition
// of the recursion). Otherwise first find the intersection of the first
// two arrays (intersect2), then repeat the whole process for that result
// combined with the remaining arrays (intersect). Thus the number of arrays
// passed as arguments to intersect is reduced by one each time, until
// there is only one array remaining.
const intersect = (xs,ys,...rest) => ys === undefined ? xs : intersect(intersect2(xs,ys),...rest);
console.log(intersect(array1, array2, array3, array4));
console.log(intersect(...arrayOfArrays));
// Alternatively, in old money,
var intersect2ES5 = function (xs, ys) {
return xs.filter(function (x) {
return ys.some(function (y) {
return y === x;
});
});
};
// Changed slightly from above, to take a single array of arrays,
// which matches the underscore.js approach in the Q., and is better anyhow.
var intersectES5 = function (zss) {
var xs = zss[0];
var ys = zss[1];
var rest = zss.slice(2);
if (ys === undefined) {
return xs;
}
return intersectES5([intersect2ES5(xs, ys)].concat(rest));
};
console.log(intersectES5([array1, array2, array3, array4]));
console.log(intersectES5(arrayOfArrays));
Using a combination of ideas from several contributors and the latest ES6 goodness, I arrived at
const array1 = ["Lorem", "ipsum", "dolor"];
const array2 = ["Lorem", "ipsum", "quick", "brown", "foo"];
const array3 = ["Jumps", "Over", "Lazy", "Lorem"];
const array4 = [1337, 420, 666, "Lorem"];
Array.prototype.intersect = function intersect(a, ...b) {
const c = function (a, b) {
b = new Set(b);
return a.filter((a) => b.has(a));
};
return undefined === a ? this : intersect.call(c(this, a), ...b);
};
console.log(array1.intersect(array2, array3, array4));
// ["Lorem"]
For anyone confused by this in the future,
_.intersection.apply(_, arrayOfArrays)
Is in fact the most elegant way to do this. But:
var arrayOfArrays = [[43, 34343, 23232], [43, 314159, 343], [43, 243]];
arrayOfArrays = _.intersection.apply(_, arrayOfArrays);
Will not work! Must do
var differentVariableName = _.intersection.apply(_,arrayOfArrays);
The cleanest way I've found to do this wasn't actually listed on this page, so here you are:
arrays[0].filter(elem => arrays.every(array => array.includes(elem)))
Reads like nice, clear english: every array includes the element. It assumes that you have at least 1 element in arrays, though. If you can't make this assumption, you can use optional chaining:
arrays?[0].filter(elem => arrays.every(array => array.includes(elem))) ?? []
Your code with _lodash is working fine.
As you can say in this fiddle:
this code:
var arrayOfArrays = [[4234, 2323, 43], [1323, 43, 1313], [23, 34, 43]];
var a = _.intersection.apply(_, arrayOfArrays);
console.log(a);
console.log(a.length);
Will have output:
[42]
1
Maybe you see
equals: function
because you are using kind of debugger.
Try to just print the array with console.log, you will get only 42.
Small recursive divide and conquer solution that does not rely on es6 or any library.
It accepts an array of arrays which makes the code shorter and allows you to pass arguments by using map.
function intersection(a) {
if (a.length > 2)
return intersection([intersection(a.slice(0, a.length / 2)), intersection(a.slice(a.length / 2))]);
if (a.length == 1)
return a[0];
return a[0].filter(function(item) {
return a[1].indexOf(item) !== -1;
});
}
var list1 = [ 'a', 'b', 'c' ];
var list2 = [ 'd', 'b', 'e' ];
var list3 = [ 'f', 'b', 'e' ];
console.log(intersection([list1, list2, list3]));
If you can use ES6 Maps and your arrays items are scalar values (easily usable as Map keys), then you can try this (works in my case) :
const intersect_lists = (lists) => {
const results = []
const lookup = new Map()
lists.map((list, idx) => {
list.map((element) => {
const count = lookup.get(element) || 0
if(count === idx) {
lookup.set(element, 1 + count)
} else {
lookup.delete(element)
}
})
})
// only elements present in all lists will have
// their respective counter equllling the total number of lists
Array.from(lookup.keys()).map((key) => {
if(lookup.get(key) === lists.length) {
results.push(key)
}
})
return results
}
Optionally you can pre-sort "lists" (of lists) by creasing length to avoid lots of iterations of the outer map() call, especially if lists lengths are heterogenous :
lists.sort((l1, l2) => l1.length - l2.length).map((list, idx) => { ... })
Sol with Maps
// nums1 = [1,2,2,1], nums2 = [2,2]
// n m
// O(nm) + space O(min(n, m))
// preprocess nums2 to a Map<number, count>
// O(n + m) + space(min(n, m))
// process the shorter one
let preprocessTarget = nums1
let loopTarget = nums2
if (nums1.length > nums2.length) {
preprocessTarget = nums2
loopTarget = nums1
}
// Map<element, number>
const countMap = new Map()
for (let num of preprocessTarget) {
if (countMap.has(num)) {
countMap.set(num, countMap.get(num) + 1)
} else {
countMap.set(num, 1)
}
}
const result = []
for (let num of loopTarget) {
if (countMap.has(num)) {
result.push(num)
const count = countMap.get(num)
if (count === 1) {
countMap.delete(num)
} else {
countMap.set(num, count - 1)
}
}
}
return result
const data = [array1, array2, array3, array4].filter(arr => arr.length > 0);
const result = [...new Set(data)];
let final = Array.of<any>();
for (const key of result) {
final = final.concat(key);
}
console.log(final);
Lodash pure:
_.keys(_.pickBy(_.groupBy(_.flatten(arrays)), function (e) {return e.length > 1}))
Lodash with plain js:
var elements = {}, duplicates = {};
_.each(arrays, function (array) {
_.each(array, function (element) {
if (!elements[element]) {
elements[element] = true;
} else {
duplicates[element] = true;
}
});
});
_.keys(duplicates);
I manage to accomplish this with a reduce call:
var intersected = intersect([[1, 2, 3], [2, 3, 4], [3, 4, 5]]);
console.log(intersected); // [3]
function intersect(arrays) {
if (0 === arrays.length) {
return [];
}
return arrays.reduce((intersection, array) => {
return intersection.filter(intersectedItem => array.some(item => intersectedItem === item));
}, arrays[0]);
}
Intersection of a variable number of arrays.
This is how I do it:
function getArraysIntersection(list1, list2, ...otherLists) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (otherLists.length) {
return getArraysIntersection(result, otherLists.shift(), ...otherLists);
}
else {
return result;
}
}
SNIPPET
function getArraysIntersection(list1, list2, ...otherLists) {
const result = [];
for (let i = 0; i < list1.length; i++) {
let item1 = list1[i];
let found = false;
for (var j = 0; j < list2.length && !found; j++) {
found = item1 === list2[j];
}
if (found === true) {
result.push(item1);
}
}
if (otherLists.length) {
return getArraysIntersection(result, otherLists.shift(), ...otherLists);
}
else {
return result;
}
}
const a = {label: "a", value: "value_A"};
const b = {label: "b", value: "value_B"};
const c = {label: "c", value: "value_C"};
const d = {label: "d", value: "value_D"};
const e = {label: "e", value: "value_E"};
const arr1 = [a,b,c];
const arr2 = [a,b,c];
const arr3 = [c];
const t0 = performance.now();
const intersection = getArraysIntersection(arr1,arr2,arr3);
const t1 = performance.now();
console.log('This took t1-t0: ' + (t1-t0).toFixed(2) + ' ms');
console.log(intersection);
const intersect = (arrayA, arrayB) => {
return arrayA.filter(elem => arrayB.includes(elem));
};
const intersectAll = (...arrays) => {
if (!Array.isArray(arrays) || arrays.length === 0) return [];
if (arrays.length === 1) return arrays[0];
return intersectAll(intersect(arrays[0], arrays[1]), ...arrays.slice(2));
};
For anyone who might need, this implements the intersection inside an array of arrays:
intersection(array) {
if (array.length === 1)
return array[0];
else {
array[1] = array[0].filter(value => array[1].includes(value));
array.shift();
return intersection(array);
}
}
function getIntersection(ar1,ar2,...arrays){
if(!ar2) return ar1
let intersection = ar1.filter(value => ar2.includes(value));
if(arrays.length ===0 ) return intersection
return getIntersection(intersection,...arrays)
}
console.log(getIntersection([1,2,3], [3,4], [5,6,3]) // [3]
Function to calculate intersection of multiple arrays in JavaScript
Write a method that creates an array of unique values that are included in all given arrays. Expected Result: ([1, 2], [2, 3]) => [2]
const arr1 = [1, 2, 1, 2, 1, 2];
const arr2 = [2, 3];
const arr3 = ["a", "b"];
const arr4 = ["b", "c"];
const arr5 = ["b", "e", "c"];
const arr6 = ["b", "b", "e"];
const arr7 = ["b", "c", "e"];
const arr8 = ["b", "e", "c"];
const intersection = (...arrays) => {
(data = [...arrays]),
(result = data.reduce((a, b) => a.filter((c) => b.includes(c))));
return [...new Set(result)];
};
console.log(intersection(arr1, arr2)); // [2]
console.log(intersection(arr3, arr4, arr5)); // ['b']
console.log(intersection(arr5, arr6, arr7, arr8)); // ['b', 'e']

Categories