Eliminate duplicates of several arrays - javascript

I have 3 arrays:
array1 = [ 'A', 'B', 'A', 'B']
array2 = [ 5, 5, 7, 5]
array3 = [true,true,true,true]
I was wondering if there is any easy way (maybe with lodash) to eliminate the duplicates and end with this:
array1 = [ 'A', 'B', 'A']
array2 = [ 5, 5, 7]
array3 = [true,true,true]
I know I can do a function and compare the previous value, but is there a more clever way to do it?
Update
Please note that I don't need to eliminate the duplicates of each array.
What I looking is a way to eliminate the duplicates "vertically"
Update 2
Please note that each "column" is a record.
record1 = ['A',5,true]
record2 = ['B',5,true]
record3 = ['A',7,true]
record1 = ['B',5,true]

TL;DR
const records = array1.map((a, i) => [a, array2[i], array3[i]]);
const index = {};
records.filter(column => {
const key = JSON.stringify(column);
return key in index ? false : index[key] = true;
});
Huh?
There are a lot of ways to solve this, with varying degrees of efficiency, and the best solution will depend on the size of your data. A simple but naΓ―ve solution iterates over each "column" and checks all of the preceding columns for equality. It looks like this:
const array1 = [ 'A', 'B', 'A', 'B'];
const array2 = [ 5, 5, 7, 5];
const array3 = [true,true,true,true];
const newArray1 = array1.slice(0,1); // column 0 is never duplicate
const newArray2 = array2.slice(0,1);
const newArray3 = array3.slice(0,1);
// loop over columns starting with index 1
outer: for (let i = 1; i < array1.length; i++) {
const a = array1[i];
const b = array2[i];
const c = array3[i];
// check all preceding columns for equality
for (let j = 0; j < i; j++) {
if (a === array1[j] && b === array2[j] && c === array3[j]) {
// duplicate; continue at top of outer loop
continue outer;
}
}
// not a duplicate; add to new arrays
newArray1.push(a);
newArray2.push(b);
newArray3.push(c);
}
console.log(newArray1);
console.log(newArray2);
console.log(newArray3);
.as-console-wrapper{min-height:100%}
As you can see, we have to check each row within each column for equality, every time. If you're curious, the complexity of this is 𝑂(𝑛(𝑛+1)/2) (technically 𝑂(π‘šπ‘›(𝑛+1)/2), where π‘š is 3 for three columns).
For a larger data sets it's advantageous to keep track of values you've already seen in a data structure that's quick to access: A hash, a.k.a. a JavaScript object. Since all of your values are primitive, a quick way to construct a key is JSON.stringify. Some might consider this a "hack"β€”and it's important to note that it will fail with values that can't be represented in JSON, e.g. Infinity or NaNβ€”but it's a fast and easy one with data this simple.
const array1 = ['A', 'B', 'A', 'B'];
const array2 = [5, 5, 7, 5];
const array3 = [true, true, true, true];
const newArray1 = [];
const newArray2 = [];
const newArray3 = [];
const index = {};
for (let i = 0; i < array1.length; i++) {
const a = array1[i];
const b = array2[i];
const c = array3[i];
const key = JSON.stringify([a,b,c]);
if (key in index) {
// duplicate; skip to top of loop
continue;
}
// not a duplicate; record in index and add to new arrays
index[key] = true;
newArray1.push(a);
newArray2.push(b);
newArray3.push(c);
}
console.log(newArray1);
console.log(newArray2);
console.log(newArray3);
.as-console-wrapper{min-height:100%}
The complexity of this is 𝑂(𝑛), or maybe 𝑂(2π‘šπ‘›) where π‘š, again,
is 3 for three columns, and the 2 is another π‘š to very roughly account for the cost of JSON.stringify. (Figuring out the cost of hash access is left as an exercise for the pedants among us; I'm content to call it 𝑂(1).)
That's still pretty verbose. Part of the reason is that using three different variables for the dataβ€”which is really a single "table"β€”leads to a lot of repetition. We can preprocess the data to make it easier to deal with. Once it's "transposed" into a single two-dimensional array, we can use Array.prototype.filter with the key technique from above, for some very terse code:
const array1 = ['A', 'B', 'A', 'B'];
const array2 = [5, 5, 7, 5];
const array3 = [true, true, true, true];
// turn "columns" into "rows" of a 2D array
const records = array1.map((a, i) => [a, array2[i], array3[i]]);
const index = {};
const newData = records.filter(column => {
const key = JSON.stringify(column);
return key in index ? false : index[key] = true;
});
console.log(newData);
.as-console-wrapper{min-height:100%}
Of course, pre-processing isn't free, so this code isn't any more performant than the more verbose version; you'll have to decide how important that is to you. If you want you can now extract the columns from newData into three variables (newData.forEach(([a,b,c]) => { newArray1.push(a); newArray2.push(b); /* ... */ })), but for many purposes the "transposed" 2D array will be easier to work with.

You can use ES6 Set https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
Set -> lets you store unique values of any type, whether primitive values or object references.
and then convert back to an array
check this snippet
const array1 = ['A','B','A','B']
const array2 = [5,5,7,5]
const array3 = [true,true,true,true]
const uniqA1= new Set(array1)
const uniqA2= new Set(array2)
const uniqA3= new Set(array3)
console.log(Array.from(uniqA1))
console.log(Array.from(uniqA2))
console.log(Array.from(uniqA3))
Hope it helps

You need to find duplicate elements with same indexes in all arrays and then filter out those elements.
var array1 = ['A', 'B', 'A', 'B'],
array2 = [5, 5, 7, 5],
array3 = [true, true, true, true];
var dupes = []
var arrays = [array1, array2, array3];
arrays.forEach(function(arr, i) {
arr.forEach((e, j) => !this[e] ? this[e] = true : dupes[i] = (dupes[i] || []).concat(j))
}, {})
var index = dupes[0].filter(e => dupes.every(a => a.includes(e)))
var result = arrays.map(e => e.filter((a, i) => !index.includes(i)))
console.log(result)

You're going to need a couple of helper functions (lodash provides them also):
let zip = (...arys) => arys[0].map((_, i) => arys.map(a => a[i]));
let uniq = (ary, key) => uniq2(ary, ary.map(key), new Set);
let uniq2 = (ary, keys, set) => ary.filter((_, i) => !set.has(keys[i]) && set.add(keys[i]))
// test
var array1 = ['A', 'B', 'A', 'B'];
var array2 = [5, 5, 7, 5];
var array3 = [true, true, true, true];
var [x, y, z] = zip(
...uniq(
zip(array1, array2, array3),
JSON.stringify
)
);
console.log(x, y, z)

Another way, with filter():
array1 = ['A', 'B', 'A', 'B'];
array2 = [5, 5, 7, 5];
array3 = [true, true, true, true];
uniqueArray1 = array1.filter(function(item, pos) {
return array1.indexOf(item) == pos;
})
uniqueArray2 = array2.filter(function(item, pos) {
return array2.indexOf(item) == pos;
})
uniqueArray3 = array3.filter(function(item, pos) {
return array3.indexOf(item) == pos;
})
console.log(uniqueArray1);
console.log(uniqueArray2);
console.log(uniqueArray3);

One method I can think of is using an object to keep track, which will also coincidentally remove any duplicates as keys have to be unique. The only thing is I can think of how to extract it back into an array for now. I will think about it tomorrow.
This utilizes jquery for deep cloning. If you want it only in vanilla javascript, you could probably just implement a deep clone function.
var array1 = [ 'A', 'B', 'A', 'B'];
var array2 = [ 5, 5, 7, 5];
var array3 = [true,true,true,true];
all_arrays = [array1, array2, array3];
let obj = {};
for (let i = 0; i < all_arrays[0].length; i++)
{
let new_obj = recursive_objects(all_arrays, 0, i)
$.extend(true, obj, new_obj);
}
console.log(obj);
function return_array(array, temp_obj)
{
let keys = Object.keys(temp_obj);
for (let key of keys)
{
}
}
function recursive_objects(arrays, arrays_index, index)
{
let obj = {}
if (arrays_index < arrays.length)
{
obj[arrays[arrays_index][index]] = recursive_objects(arrays, ++arrays_index, index);
}
return obj;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

React js Array and Array Reverse issue without mutating original array [duplicate]

Array.prototype.reverse reverses the contents of an array in place (with mutation)...
Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation)?
You can use slice() to make a copy then reverse() it
var newarray = array.slice().reverse();
var array = ['a', 'b', 'c', 'd', 'e'];
var newarray = array.slice().reverse();
console.log('a', array);
console.log('na', newarray);
In ES6:
const newArray = [...array].reverse()
Another ES6 variant:
We can also use .reduceRight() to create a reversed array without actually reversing it.
let A = ['a', 'b', 'c', 'd', 'e', 'f'];
let B = A.reduceRight((a, c) => (a.push(c), a), []);
console.log(B);
Useful Resources:
Array.prototype.reduceRight()
Arrow Functions
Comma Operator
const originalArray = ['a', 'b', 'c', 'd', 'e', 'f'];
const newArray = Array.from(originalArray).reverse();
console.log(newArray);
There are multiple ways of reversing an array without modifying. Two of them are
var array = [1,2,3,4,5,6,7,8,9,10];
// Using Splice
var reverseArray1 = array.splice().reverse(); // Fastest
// Using spread operator
var reverseArray2 = [...array].reverse();
// Using for loop
var reverseArray3 = [];
for(var i = array.length-1; i>=0; i--) {
reverseArray.push(array[i]);
}
Performance test http://jsben.ch/guftu
Try this recursive solution:
const reverse = ([head, ...tail]) =>
tail.length === 0
? [head] // Base case -- cannot reverse a single element.
: [...reverse(tail), head] // Recursive case
reverse([1]); // [1]
reverse([1,2,3]); // [3,2,1]
reverse('hello').join(''); // 'olleh' -- Strings too!
An ES6 alternative using .reduce() and spreading.
const foo = [1, 2, 3, 4];
const bar = foo.reduce((acc, b) => ([b, ...acc]), []);
Basically what it does is create a new array with the next element in foo, and spreading the accumulated array for each iteration after b.
[]
[1] => [1]
[2, ...[1]] => [2, 1]
[3, ...[2, 1]] => [3, 2, 1]
[4, ...[3, 2, 1]] => [4, 3, 2, 1]
Alternatively .reduceRight() as mentioned above here, but without the .push() mutation.
const baz = foo.reduceRight((acc, b) => ([...acc, b]), []);
const arrayCopy = Object.assign([], array).reverse()
This solution:
-Successfully copies the array
-Doesn't mutate the original array
-Looks like it's doing what it is doing
There's a new tc39 proposal, which adds a toReversed method to Array that returns a copy of the array and doesn't modify the original.
Example from the proposal:
const sequence = [1, 2, 3];
sequence.toReversed(); // => [3, 2, 1]
sequence; // => [1, 2, 3]
As it's currently in stage 3, it will likely be implemented in browser engines soon, but in the meantime a polyfill is available here or in core-js.
Reversing in place with variable swap just for demonstrative purposes (but you need a copy if you don't want to mutate)
const myArr = ["a", "b", "c", "d"];
const copy = [...myArr];
for (let i = 0; i < (copy.length - 1) / 2; i++) {
const lastIndex = copy.length - 1 - i;
[copy[i], copy[lastIndex]] = [copy[lastIndex], copy[i]]
}
Jumping into 2022, and here's the most efficient solution today (highest-performing, and no extra memory usage).
For any ArrayLike type, the fastest way to reverse is logically, by wrapping it into a reversed iterable:
function reverse<T>(input: ArrayLike<T>): Iterable<T> {
return {
[Symbol.iterator](): Iterator<T> {
let i = input.length;
return {
next(): IteratorResult<T> {
return i
? {value: input[--i], done: false}
: {value: undefined, done: true};
},
};
},
};
}
This way you can reverse-iterate through any Array, string or Buffer, without any extra copy or processing for the reversed data:
for(const a of reverse([1, 2, 3])) {
console.log(a); //=> 3 2 1
}
It is the fastest approach, because you do not copy the data, and do no processing at all, you just reverse it logically.
Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation) ?
Yes, there is a way to achieve this by using to[Operation] that return a new collection with the operation applied (This is currently at stage 3, will be available soon).
Implementation will be like :
const arr = [5, 4, 3, 2, 1];
const reversedArr = arr.toReverse();
console.log(arr); // [5, 4, 3, 2, 1]
console.log(reversedArr); // [1, 2, 3, 4, 5]
Not the best solution but it works
Array.prototype.myNonMutableReverse = function () {
const reversedArr = [];
for (let i = this.length - 1; i >= 0; i--) reversedArr.push(this[i]);
return reversedArr;
};
const a = [1, 2, 3, 4, 5, 6, 7, 8];
const b = a.myNonMutableReverse();
console.log("a",a);
console.log("////////")
console.log("b",b);
INTO plain Javascript:
function reverseArray(arr, num) {
var newArray = [];
for (let i = num; i <= arr.length - 1; i++) {
newArray.push(arr[i]);
}
return newArray;
}
es6:
const reverseArr = [1,2,3,4].sort(()=>1)

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 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']

Reverse array in Javascript without mutating original array

Array.prototype.reverse reverses the contents of an array in place (with mutation)...
Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation)?
You can use slice() to make a copy then reverse() it
var newarray = array.slice().reverse();
var array = ['a', 'b', 'c', 'd', 'e'];
var newarray = array.slice().reverse();
console.log('a', array);
console.log('na', newarray);
In ES6:
const newArray = [...array].reverse()
Another ES6 variant:
We can also use .reduceRight() to create a reversed array without actually reversing it.
let A = ['a', 'b', 'c', 'd', 'e', 'f'];
let B = A.reduceRight((a, c) => (a.push(c), a), []);
console.log(B);
Useful Resources:
Array.prototype.reduceRight()
Arrow Functions
Comma Operator
const originalArray = ['a', 'b', 'c', 'd', 'e', 'f'];
const newArray = Array.from(originalArray).reverse();
console.log(newArray);
There are multiple ways of reversing an array without modifying. Two of them are
var array = [1,2,3,4,5,6,7,8,9,10];
// Using Splice
var reverseArray1 = array.splice().reverse(); // Fastest
// Using spread operator
var reverseArray2 = [...array].reverse();
// Using for loop
var reverseArray3 = [];
for(var i = array.length-1; i>=0; i--) {
reverseArray.push(array[i]);
}
Performance test http://jsben.ch/guftu
Try this recursive solution:
const reverse = ([head, ...tail]) =>
tail.length === 0
? [head] // Base case -- cannot reverse a single element.
: [...reverse(tail), head] // Recursive case
reverse([1]); // [1]
reverse([1,2,3]); // [3,2,1]
reverse('hello').join(''); // 'olleh' -- Strings too!
An ES6 alternative using .reduce() and spreading.
const foo = [1, 2, 3, 4];
const bar = foo.reduce((acc, b) => ([b, ...acc]), []);
Basically what it does is create a new array with the next element in foo, and spreading the accumulated array for each iteration after b.
[]
[1] => [1]
[2, ...[1]] => [2, 1]
[3, ...[2, 1]] => [3, 2, 1]
[4, ...[3, 2, 1]] => [4, 3, 2, 1]
Alternatively .reduceRight() as mentioned above here, but without the .push() mutation.
const baz = foo.reduceRight((acc, b) => ([...acc, b]), []);
const arrayCopy = Object.assign([], array).reverse()
This solution:
-Successfully copies the array
-Doesn't mutate the original array
-Looks like it's doing what it is doing
There's a new tc39 proposal, which adds a toReversed method to Array that returns a copy of the array and doesn't modify the original.
Example from the proposal:
const sequence = [1, 2, 3];
sequence.toReversed(); // => [3, 2, 1]
sequence; // => [1, 2, 3]
As it's currently in stage 3, it will likely be implemented in browser engines soon, but in the meantime a polyfill is available here or in core-js.
Reversing in place with variable swap just for demonstrative purposes (but you need a copy if you don't want to mutate)
const myArr = ["a", "b", "c", "d"];
const copy = [...myArr];
for (let i = 0; i < (copy.length - 1) / 2; i++) {
const lastIndex = copy.length - 1 - i;
[copy[i], copy[lastIndex]] = [copy[lastIndex], copy[i]]
}
Jumping into 2022, and here's the most efficient solution today (highest-performing, and no extra memory usage).
For any ArrayLike type, the fastest way to reverse is logically, by wrapping it into a reversed iterable:
function reverse<T>(input: ArrayLike<T>): Iterable<T> {
return {
[Symbol.iterator](): Iterator<T> {
let i = input.length;
return {
next(): IteratorResult<T> {
return i
? {value: input[--i], done: false}
: {value: undefined, done: true};
},
};
},
};
}
This way you can reverse-iterate through any Array, string or Buffer, without any extra copy or processing for the reversed data:
for(const a of reverse([1, 2, 3])) {
console.log(a); //=> 3 2 1
}
It is the fastest approach, because you do not copy the data, and do no processing at all, you just reverse it logically.
Is there a similarly simple strategy for reversing an array without altering the contents of the original array (without mutation) ?
Yes, there is a way to achieve this by using to[Operation] that return a new collection with the operation applied (This is currently at stage 3, will be available soon).
Implementation will be like :
const arr = [5, 4, 3, 2, 1];
const reversedArr = arr.toReverse();
console.log(arr); // [5, 4, 3, 2, 1]
console.log(reversedArr); // [1, 2, 3, 4, 5]
Not the best solution but it works
Array.prototype.myNonMutableReverse = function () {
const reversedArr = [];
for (let i = this.length - 1; i >= 0; i--) reversedArr.push(this[i]);
return reversedArr;
};
const a = [1, 2, 3, 4, 5, 6, 7, 8];
const b = a.myNonMutableReverse();
console.log("a",a);
console.log("////////")
console.log("b",b);
INTO plain Javascript:
function reverseArray(arr, num) {
var newArray = [];
for (let i = num; i <= arr.length - 1; i++) {
newArray.push(arr[i]);
}
return newArray;
}
es6:
const reverseArr = [1,2,3,4].sort(()=>1)

How to search for multiple index(es) of same values in javascript array

I have a 1 dimensional array like:
var abc = ['a','a','b','a','c']
Now I want to get back all the indexes of 'a', that is 0, 1 and 3.
Are there any simple solutions?
P.S.
I know IndexOf or jQuery.inArray(). But they just returned the index of first matched element only
You could extend the basic Array Object with the following method:
Array.prototype.multiIndexOf = function (el) {
var idxs = [];
for (var i = this.length - 1; i >= 0; i--) {
if (this[i] === el) {
idxs.unshift(i);
}
}
return idxs;
};
Then the operation
var abc = ['a','a','b','a','c'];
abc.multiIndexOf('a');
would give you the result:
[0, 1, 3]
Jsperf comparison of unshift / push / push(reverse order)
You could use Array#reduce with Array#concat with a check for the wanted item, take the index or an empty array.
var abc = ['a', 'a', 'b', 'a', 'c'],
indices = abc.reduce((r, v, i) => r.concat(v === 'a' ? i : []), []);
console.log(indices);
ES5
var abc = ['a', 'a', 'b', 'a', 'c'],
indices = abc.reduce(function (r, v, i) {
return r.concat(v === 'a' ? i : []);
}, []);
console.log(indices);
Rather than using a for loop, you can use a while loop combined with indexOf:
var array = [1, 2, 3, 4, 2, 8, 5],
value = 2,
i = -1,
indizes = [];
while((i = array.indexOf(value, i + 1)) !== -1) {
indizes.push(i);
}
This will return you [1, 4] and of course could be combined with extending the prototype of Array.
The second argument of indexOf specifies where to start the search in the given array.
You can take advantage of the fact that $.map() does not push values in its resulting array when the function you pass returns undefined.
Therefore, you can write:
var abc = ["a", "a", "b", "a", "c"];
var indices = $.map(abc, function(element, index) {
if (element == "a") {
return index;
}
});
You also use reduce function on the array and push the indexes to accumulated array, you need start with an empty array, the good thing about reduce is it's async and also time execution is faster than for loop, also it's a native function on array, look at the below, hope it's helping:
var arr = [0, 1, 2, 3, 7, 2, 3, 4, 7, 8, 9, 2, 3];
function indexesOf(num) {
var reduced = arr.reduce(function(acc, val, ind, arr){
if(val === num){
acc.push(ind);
}
return acc;
}, []);
return reduced;
}
indexesOf(2); //[2, 5, 11]
AFAIK, there's no Javascript or jQuery function that does this in one step, you have to write a loop.
var indexes = [];
$.each(abc, function(i, val) {
if (val == "a") {
indexes.push(i);
}
}
Do it this way :
var abc = ['a','a','b','a','c'];
for (var i=0; i<abc.length; i++) {if(abc[i]=='a') {console.log(i)};}
If your array size is fixed, then you can find the first occurrence in the array using indexOf(). Use the found index value as starting point in indexOf() to find an other occurrence.
var firstOccurance = [your_array].indexOf(2)
var secondOccurance = [your_array].indexOf(2, firstOccurance + 1)
Demo
use for loop
var arr = ['a', 'a', 'b', 'a', 'c'];
var indexA = [];
for (var i = 0; i < arr.length; i++) {
if ("a" == arr[i]) indexA.push(i)
}
With ES6 syntax you could go with forEach and the ternary operator :
const abc = ['a','a','b','a','c']
let matchingIndexes = []
abc.forEach( (currentItem, index) => {
currentItem === 'a' ? matchingIndexes.push(index) : null
})
console.log(matchingIndexes) // [0, 1, 3]

Categories