Quickest way to check if 2 arrays contain same values in javascript - javascript

Is there a quicker or more efficient way to check that two arrays contain the same values in javascript?
Here is what I'm currently doing to check this. It works, but is lengthy.
var arraysAreDifferent = false;
for (var i = 0; i < array1.length; i++) {
if (!array2.includes(array1[i])) {
arraysAreDifferent = true;
}
}
for (var i = 0; i < array2.length; i++) {
if (!array1.includes(array2[i])) {
arraysAreDifferent = true;
}
}

To reduce computational complexity from O(n ^ 2) to O(n), use Sets instead - Set.has is O(1), but Array.includes is O(n).
Rather than a regular for loop's verbose manual iteration, use .every to check if every item in an array passes a test. Also check that both Set's sizes are the same - if that's done, then if one of the arrays is iterated over, there's no need to iterate over the other (other than for the construction of its Set):
const arr1Set = new Set(array1);
const arr2Set = new Set(array2);
const arraysAreDifferent = (
arr1Set.size === arr2Set.size &&
array1.every(item => arr2Set.has(item))
);

function same(arr1, arr2){
//----if you want to check by length as well
// if(arr1.length != arr2.length){
// return false;
// }
// creating an object with key => arr1 value and value => number of time that value repeat;
let frequencyCounter1 = {};
let frequencyCounter2 = {};
for(let val of arr1){
frequencyCounter1[val] = (frequencyCounter1[val] || 0) + 1;
}
for(let val of arr2){
frequencyCounter2[val] = (frequencyCounter2[val] || 0) + 1;
}
for(let key in frequencyCounter1){
//check if the key is present in arr2 or not
if(!(key in frequencyCounter2)) return false;
//check the number of times the value repetiton is same or not;
if(frequencyCounter2[key]!==frequencyCounter1[key]) return false;
}
return true;
}

Related

compare 2d array with 1d array in javascript

var player1=["t1", "t9", "t7", "t8", "t2"];
var player2=["t5", "t3", "t4", "t6"];
var winmoves=[[t1,t2,t3],[t4,t5,t6],[t7,t8,t9],[t1,t4,t7],[t2,t5,t8],[t3,t6,t9],[t1,t5,t9],[t3,t5,t7]];
else if (moves>=3 && moves<=9) {
moves+=1;
if (turn==1) {
var i=0;
turn=2;
x.innerHTML="<img src='x.png'/>";
player1.push(x.id);
while(i<=moves){
if (winmoves[i] in player1) {
alert("player1 wins");
document.getElementById("player1").innerHTML=1;
}
i+=1;
}
}
i have 1d array player1 and 2d array winmoves in javascript and i want to check that w[0]'s all values are present in p and so on for w[1],w[2],etc.
if condition with (winmoves[i] in player1) is not working.
i don't know if i am writing this write.
help me guys i am stuck here how can i do so.
It is not working even after i have made these changes.
else if (moves>=3 && moves<9) {
moves+=1;
if (turn==1) {
var i=0;
turn=2;
x.innerHTML="<img src='x.png'/>";
player1.push(x.id);
while(i<=moves){
mapped1 = winmoves.map(a1 => a1.every(e1 => player1.includes(e1)));
if (mapped1[i]) {
alert("player1 wins");
document.getElementById("player1").innerHTML=1;
}
i+=1;
}
}
else if (turn==2) {
turn=1;
var i=0;
x.innerHTML="<img src='o.png'/>";
turn=1;
player2.push(x.id);
while(i<=moves)
{
mapped2 = winmoves.map(a => a.every(e => player2.includes(e)));
if (mapped2[i]) {
alert("player2 wins");
document.getElementById("player2").innerHTML=1;
}
i+=1;
}
}
}
I would simply do this with a simple invention of Array.prototype.intersect() and the rest is such a straightforward single liner.
Array.prototype.intersect = function(a) {
return this.filter(e => a.includes(e));
};
var player1 = ["t1","t2","t3","t5","t7"],
winmoves = [["t1","t2","t3"],["t4","t5","t6"],["t7","t8","t9"],["t1","t4","t7"],["t2","t5","t8"],["t3","t6","t9"],["t1","t5","t9"],["t3","t5","t7"]];
filtered = winmoves.filter(a => a.intersect(player1).length == a.length);
mapped = winmoves.map(a => a.intersect(player1).length == a.length);
console.log(filtered);
console.log(mapped);
OK we have a generic Array method which finds the intersection of two arrays. (inbetween the array it's called upon and the one provided as argument) It's basically a filter checking each item of first array to see whether it is included in the second array. So we filter out the items exist in both arrays.
To obtain the filtered array we utilize Array.prototype.filter() again. This time our first array is winmoves which includes arrays that we will check for each the intersection with player1 array. If the intersection length is equal to winmove's item's length that means all elements of winmove's item is existing in the player1 array. So we return that array item to the filtered.
Specific to your case without using an intersect method you can utilize Array.prototype.every() as follows;
var player1 = ["t1","t2","t3","t5","t7"],
winmoves = [["t1","t2","t3"],["t4","t5","t6"],["t7","t8","t9"],["t1","t4","t7"],["t2","t5","t8"],["t3","t6","t9"],["t1","t5","t9"],["t3","t5","t7"]];
filtered = winmoves.filter(a => a.every(e => player1.includes(e)));
mapped = winmoves.map(a => a.every(e => player1.includes(e)));
console.log(filtered);
console.log(mapped);
What you could do is use nested for loops
for(var j = 0, j < elemInP, j++){
int flag = 0;
for(var x = 0, x < elemInWx, x++){
for(var y = 0, y < elemInWy, y++){
if(p[j] == w[x][y]){
flag = 1;
/*There is no need to run this loop once the value has been found*/
break;
}
}
if(flag){
/*If we have found the value no need to keep looking*/
break;
}
}
if(!flag){
print p[j] is not in w;
}
}
This is just a general idea of one way to compare the two arrays. The actual syntax of the code will need to be edited to work in JavaScript as this is just basic pseudocode. Flag is just a variable that holds if the value was found or not and is reset for each new value. For efficiency, you could have a break/return after setting flag = 1, but this is

Compare multiple arrays for common values [duplicate]

What's the simplest, library-free code for implementing array intersections in javascript? I want to write
intersection([1,2,3], [2,3,4,5])
and get
[2, 3]
Use a combination of Array.prototype.filter and Array.prototype.includes:
const filteredArray = array1.filter(value => array2.includes(value));
For older browsers, with Array.prototype.indexOf and without an arrow function:
var filteredArray = array1.filter(function(n) {
return array2.indexOf(n) !== -1;
});
NB! Both .includes and .indexOf internally compares elements in the array by using ===, so if the array contains objects it will only compare object references (not their content). If you want to specify your own comparison logic, use Array.prototype.some instead.
Destructive seems simplest, especially if we can assume the input is sorted:
/* destructively finds the intersection of
* two arrays in a simple fashion.
*
* PARAMS
* a - first array, must already be sorted
* b - second array, must already be sorted
*
* NOTES
* State of input arrays is undefined when
* the function returns. They should be
* (prolly) be dumped.
*
* Should have O(n) operations, where n is
* n = MIN(a.length, b.length)
*/
function intersection_destructive(a, b)
{
var result = [];
while( a.length > 0 && b.length > 0 )
{
if (a[0] < b[0] ){ a.shift(); }
else if (a[0] > b[0] ){ b.shift(); }
else /* they're equal */
{
result.push(a.shift());
b.shift();
}
}
return result;
}
Non-destructive has to be a hair more complicated, since we’ve got to track indices:
/* finds the intersection of
* two arrays in a simple fashion.
*
* PARAMS
* a - first array, must already be sorted
* b - second array, must already be sorted
*
* NOTES
*
* Should have O(n) operations, where n is
* n = MIN(a.length(), b.length())
*/
function intersect_safe(a, b)
{
var ai=0, bi=0;
var result = [];
while( ai < a.length && bi < b.length )
{
if (a[ai] < b[bi] ){ ai++; }
else if (a[ai] > b[bi] ){ bi++; }
else /* they're equal */
{
result.push(a[ai]);
ai++;
bi++;
}
}
return result;
}
If your environment supports ECMAScript 6 Set, one simple and supposedly efficient (see specification link) way:
function intersect(a, b) {
var setA = new Set(a);
var setB = new Set(b);
var intersection = new Set([...setA].filter(x => setB.has(x)));
return Array.from(intersection);
}
Shorter, but less readable (also without creating the additional intersection Set):
function intersect(a, b) {
var setB = new Set(b);
return [...new Set(a)].filter(x => setB.has(x));
}
Note that when using sets you will only get distinct values, thus new Set([1, 2, 3, 3]).size evaluates to 3.
Using Underscore.js or lodash.js
_.intersection( [0,345,324] , [1,0,324] ) // gives [0,324]
// Return elements of array a that are also in b in linear time:
function intersect(a, b) {
return a.filter(Set.prototype.has, new Set(b));
}
// Example:
console.log(intersect([1,2,3], [2,3,4,5]));
I recommend above succinct solution which outperforms other implementations on large inputs. If performance on small inputs matters, check the alternatives below.
Alternatives and performance comparison:
See the following snippet for alternative implementations and check https://jsperf.com/array-intersection-comparison for performance comparisons.
function intersect_for(a, b) {
const result = [];
const alen = a.length;
const blen = b.length;
for (let i = 0; i < alen; ++i) {
const ai = a[i];
for (let j = 0; j < blen; ++j) {
if (ai === b[j]) {
result.push(ai);
break;
}
}
}
return result;
}
function intersect_filter_indexOf(a, b) {
return a.filter(el => b.indexOf(el) !== -1);
}
function intersect_filter_in(a, b) {
const map = b.reduce((map, el) => {map[el] = true; return map}, {});
return a.filter(el => el in map);
}
function intersect_for_in(a, b) {
const result = [];
const map = {};
for (let i = 0, length = b.length; i < length; ++i) {
map[b[i]] = true;
}
for (let i = 0, length = a.length; i < length; ++i) {
if (a[i] in map) result.push(a[i]);
}
return result;
}
function intersect_filter_includes(a, b) {
return a.filter(el => b.includes(el));
}
function intersect_filter_has_this(a, b) {
return a.filter(Set.prototype.has, new Set(b));
}
function intersect_filter_has_arrow(a, b) {
const set = new Set(b);
return a.filter(el => set.has(el));
}
function intersect_for_has(a, b) {
const result = [];
const set = new Set(b);
for (let i = 0, length = a.length; i < length; ++i) {
if (set.has(a[i])) result.push(a[i]);
}
return result;
}
Results in Firefox 53:
Ops/sec on large arrays (10,000 elements):
filter + has (this) 523 (this answer)
for + has 482
for-loop + in 279
filter + in 242
for-loops 24
filter + includes 14
filter + indexOf 10
Ops/sec on small arrays (100 elements):
for-loop + in 384,426
filter + in 192,066
for-loops 159,137
filter + includes 104,068
filter + indexOf 71,598
filter + has (this) 43,531 (this answer)
filter + has (arrow function) 35,588
My contribution in ES6 terms. In general it finds the intersection of an array with indefinite number of arrays provided as arguments.
Array.prototype.intersect = function(...a) {
return [this,...a].reduce((p,c) => p.filter(e => c.includes(e)));
}
var arrs = [[0,2,4,6,8],[4,5,6,7],[4,6]],
arr = [0,1,2,3,4,5,6,7,8,9];
document.write("<pre>" + JSON.stringify(arr.intersect(...arrs)) + "</pre>");
How about just using associative arrays?
function intersect(a, b) {
var d1 = {};
var d2 = {};
var results = [];
for (var i = 0; i < a.length; i++) {
d1[a[i]] = true;
}
for (var j = 0; j < b.length; j++) {
d2[b[j]] = true;
}
for (var k in d1) {
if (d2[k])
results.push(k);
}
return results;
}
edit:
// new version
function intersect(a, b) {
var d = {};
var results = [];
for (var i = 0; i < b.length; i++) {
d[b[i]] = true;
}
for (var j = 0; j < a.length; j++) {
if (d[a[j]])
results.push(a[j]);
}
return results;
}
The performance of #atk's implementation for sorted arrays of primitives can be improved by using .pop rather than .shift.
function intersect(array1, array2) {
var result = [];
// Don't destroy the original arrays
var a = array1.slice(0);
var b = array2.slice(0);
var aLast = a.length - 1;
var bLast = b.length - 1;
while (aLast >= 0 && bLast >= 0) {
if (a[aLast] > b[bLast] ) {
a.pop();
aLast--;
} else if (a[aLast] < b[bLast] ){
b.pop();
bLast--;
} else /* they're equal */ {
result.push(a.pop());
b.pop();
aLast--;
bLast--;
}
}
return result;
}
I created a benchmark using jsPerf. It's about three times faster to use .pop.
If you need to have it handle intersecting multiple arrays:
const intersect = (a1, a2, ...rest) => {
const a12 = a1.filter(value => a2.includes(value))
if (rest.length === 0) { return a12; }
return intersect(a12, ...rest);
};
console.log(intersect([1,2,3,4,5], [1,2], [1, 2, 3,4,5], [2, 10, 1]))
Sort it
check one by one from the index 0, create new array from that.
Something like this, Not tested well though.
function intersection(x,y){
x.sort();y.sort();
var i=j=0;ret=[];
while(i<x.length && j<y.length){
if(x[i]<y[j])i++;
else if(y[j]<x[i])j++;
else {
ret.push(x[i]);
i++,j++;
}
}
return ret;
}
alert(intersection([1,2,3], [2,3,4,5]));
PS:The algorithm only intended for Numbers and Normal Strings, intersection of arbitary object arrays may not work.
Using jQuery:
var a = [1,2,3];
var b = [2,3,4,5];
var c = $(b).not($(b).not(a));
alert(c);
A tiny tweak to the smallest one here (the filter/indexOf solution), namely creating an index of the values in one of the arrays using a JavaScript object, will reduce it from O(N*M) to "probably" linear time. source1 source2
function intersect(a, b) {
var aa = {};
a.forEach(function(v) { aa[v]=1; });
return b.filter(function(v) { return v in aa; });
}
This isn't the very simplest solution (it's more code than filter+indexOf), nor is it the very fastest (probably slower by a constant factor than intersect_safe()), but seems like a pretty good balance. It is on the very simple side, while providing good performance, and it doesn't require pre-sorted inputs.
For arrays containing only strings or numbers you can do something with sorting, as per some of the other answers. For the general case of arrays of arbitrary objects I don't think you can avoid doing it the long way. The following will give you the intersection of any number of arrays provided as parameters to arrayIntersection:
var arrayContains = Array.prototype.indexOf ?
function(arr, val) {
return arr.indexOf(val) > -1;
} :
function(arr, val) {
var i = arr.length;
while (i--) {
if (arr[i] === val) {
return true;
}
}
return false;
};
function arrayIntersection() {
var val, arrayCount, firstArray, i, j, intersection = [], missing;
var arrays = Array.prototype.slice.call(arguments); // Convert arguments into a real array
// Search for common values
firstArray = arrays.pop();
if (firstArray) {
j = firstArray.length;
arrayCount = arrays.length;
while (j--) {
val = firstArray[j];
missing = false;
// Check val is present in each remaining array
i = arrayCount;
while (!missing && i--) {
if ( !arrayContains(arrays[i], val) ) {
missing = true;
}
}
if (!missing) {
intersection.push(val);
}
}
}
return intersection;
}
arrayIntersection( [1, 2, 3, "a"], [1, "a", 2], ["a", 1] ); // Gives [1, "a"];
Simplest, fastest O(n) and shortest way:
function intersection (a, b) {
const setA = new Set(a);
return b.filter(value => setA.has(value));
}
console.log(intersection([1,2,3], [2,3,4,5]))
#nbarbosa has almost the same answer but he cast both arrays to Set and then back to array. There is no need for any extra casting.
Another indexed approach able to process any number of arrays at once:
// Calculate intersection of multiple array or object values.
function intersect (arrList) {
var arrLength = Object.keys(arrList).length;
// (Also accepts regular objects as input)
var index = {};
for (var i in arrList) {
for (var j in arrList[i]) {
var v = arrList[i][j];
if (index[v] === undefined) index[v] = 0;
index[v]++;
};
};
var retv = [];
for (var i in index) {
if (index[i] == arrLength) retv.push(i);
};
return retv;
};
It works only for values that can be evaluated as strings and you should pass them as an array like:
intersect ([arr1, arr2, arr3...]);
...but it transparently accepts objects as parameter or as any of the elements to be intersected (always returning array of common values). Examples:
intersect ({foo: [1, 2, 3, 4], bar: {a: 2, j:4}}); // [2, 4]
intersect ([{x: "hello", y: "world"}, ["hello", "user"]]); // ["hello"]
EDIT: I just noticed that this is, in a way, slightly buggy.
That is: I coded it thinking that input arrays cannot itself contain repetitions (as provided example doesn't).
But if input arrays happen to contain repetitions, that would produce wrong results. Example (using below implementation):
intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]);
// Expected: [ '1' ]
// Actual: [ '1', '3' ]
Fortunately this is easy to fix by simply adding second level indexing. That is:
Change:
if (index[v] === undefined) index[v] = 0;
index[v]++;
by:
if (index[v] === undefined) index[v] = {};
index[v][i] = true; // Mark as present in i input.
...and:
if (index[i] == arrLength) retv.push(i);
by:
if (Object.keys(index[i]).length == arrLength) retv.push(i);
Complete example:
// Calculate intersection of multiple array or object values.
function intersect (arrList) {
var arrLength = Object.keys(arrList).length;
// (Also accepts regular objects as input)
var index = {};
for (var i in arrList) {
for (var j in arrList[i]) {
var v = arrList[i][j];
if (index[v] === undefined) index[v] = {};
index[v][i] = true; // Mark as present in i input.
};
};
var retv = [];
for (var i in index) {
if (Object.keys(index[i]).length == arrLength) retv.push(i);
};
return retv;
};
intersect ([[1, 3, 4, 6, 3], [1, 8, 99]]); // [ '1' ]
With some restrictions on your data, you can do it in linear time!
For positive integers: use an array mapping the values to a "seen/not seen" boolean.
function intersectIntegers(array1,array2) {
var seen=[],
result=[];
for (var i = 0; i < array1.length; i++) {
seen[array1[i]] = true;
}
for (var i = 0; i < array2.length; i++) {
if ( seen[array2[i]])
result.push(array2[i]);
}
return result;
}
There is a similar technique for objects: take a dummy key, set it to "true" for each element in array1, then look for this key in elements of array2. Clean up when you're done.
function intersectObjects(array1,array2) {
var result=[];
var key="tmpKey_intersect"
for (var i = 0; i < array1.length; i++) {
array1[i][key] = true;
}
for (var i = 0; i < array2.length; i++) {
if (array2[i][key])
result.push(array2[i]);
}
for (var i = 0; i < array1.length; i++) {
delete array1[i][key];
}
return result;
}
Of course you need to be sure the key didn't appear before, otherwise you'll be destroying your data...
function intersection(A,B){
var result = new Array();
for (i=0; i<A.length; i++) {
for (j=0; j<B.length; j++) {
if (A[i] == B[j] && $.inArray(A[i],result) == -1) {
result.push(A[i]);
}
}
}
return result;
}
For simplicity:
// Usage
const intersection = allLists
.reduce(intersect, allValues)
.reduce(removeDuplicates, []);
// Implementation
const intersect = (intersection, list) =>
intersection.filter(item =>
list.some(x => x === item));
const removeDuplicates = (uniques, item) =>
uniques.includes(item) ? uniques : uniques.concat(item);
// Example Data
const somePeople = [bob, doug, jill];
const otherPeople = [sarah, bob, jill];
const morePeople = [jack, jill];
const allPeople = [...somePeople, ...otherPeople, ...morePeople];
const allGroups = [somePeople, otherPeople, morePeople];
// Example Usage
const intersection = allGroups
.reduce(intersect, allPeople)
.reduce(removeDuplicates, []);
intersection; // [jill]
Benefits:
dirt simple
data-centric
works for arbitrary number of lists
works for arbitrary lengths of lists
works for arbitrary types of values
works for arbitrary sort order
retains shape (order of first appearance in any array)
exits early where possible
memory safe, short of tampering with Function / Array prototypes
Drawbacks:
higher memory usage
higher CPU usage
requires an understanding of reduce
requires understanding of data flow
You wouldn't want to use this for 3D engine or kernel work, but if you have problems getting this to run in an event-based app, your design has bigger problems.
I'll contribute with what has been working out best for me:
if (!Array.prototype.intersect){
Array.prototype.intersect = function (arr1) {
var r = [], o = {}, l = this.length, i, v;
for (i = 0; i < l; i++) {
o[this[i]] = true;
}
l = arr1.length;
for (i = 0; i < l; i++) {
v = arr1[i];
if (v in o) {
r.push(v);
}
}
return r;
};
}
A functional approach with ES2015
A functional approach must consider using only pure functions without side effects, each of which is only concerned with a single job.
These restrictions enhance the composability and reusability of the functions involved.
// small, reusable auxiliary functions
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const apply = f => x => f(x);
// intersection
const intersect = xs => ys => {
const zs = createSet(ys);
return filter(x => zs.has(x)
? true
: false
) (xs);
};
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
// run it
console.log( intersect(xs) (ys) );
Please note that the native Set type is used, which has an advantageous
lookup performance.
Avoid duplicates
Obviously repeatedly occurring items from the first Array are preserved, while the second Array is de-duplicated. This may be or may be not the desired behavior. If you need a unique result just apply dedupe to the first argument:
// auxiliary functions
const apply = f => x => f(x);
const comp = f => g => x => f(g(x));
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
// intersection
const intersect = xs => ys => {
const zs = createSet(ys);
return filter(x => zs.has(x)
? true
: false
) (xs);
};
// de-duplication
const dedupe = comp(afrom) (createSet);
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
// unique result
console.log( intersect(dedupe(xs)) (ys) );
Compute the intersection of any number of Arrays
If you want to compute the intersection of an arbitrarily number of Arrays just compose intersect with foldl. Here is a convenience function:
// auxiliary functions
const apply = f => x => f(x);
const uncurry = f => (x, y) => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);
// intersection
const intersect = xs => ys => {
const zs = createSet(ys);
return filter(x => zs.has(x)
? true
: false
) (xs);
};
// intersection of an arbitrarily number of Arrays
const intersectn = (head, ...tail) => foldl(intersect) (head) (tail);
// mock data
const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];
const zs = [0,1,2,3,4,5,6];
// run
console.log( intersectn(xs, ys, zs) );
.reduce to build a map, and .filter to find the intersection. delete within the .filter allows us to treat the second array as though it's a unique set.
function intersection (a, b) {
var seen = a.reduce(function (h, k) {
h[k] = true;
return h;
}, {});
return b.filter(function (k) {
var exists = seen[k];
delete seen[k];
return exists;
});
}
I find this approach pretty easy to reason about. It performs in constant time.
I have written an intesection function which can even detect intersection of array of objects based on particular property of those objects.
For instance,
if arr1 = [{id: 10}, {id: 20}]
and arr2 = [{id: 20}, {id: 25}]
and we want intersection based on the id property, then the output should be :
[{id: 20}]
As such, the function for the same (note: ES6 code) is :
const intersect = (arr1, arr2, accessors = [v => v, v => v]) => {
const [fn1, fn2] = accessors;
const set = new Set(arr2.map(v => fn2(v)));
return arr1.filter(value => set.has(fn1(value)));
};
and you can call the function as:
intersect(arr1, arr2, [elem => elem.id, elem => elem.id])
Also note: this function finds intersection considering the first array is the primary array and thus the intersection result will be that of the primary array.
This function avoids the N^2 problem, taking advantage of the power of dictionaries. Loops through each array only once, and a third and shorter loop to return the final result.
It also supports numbers, strings, and objects.
function array_intersect(array1, array2)
{
var mergedElems = {},
result = [];
// Returns a unique reference string for the type and value of the element
function generateStrKey(elem) {
var typeOfElem = typeof elem;
if (typeOfElem === 'object') {
typeOfElem += Object.prototype.toString.call(elem);
}
return [typeOfElem, elem.toString(), JSON.stringify(elem)].join('__');
}
array1.forEach(function(elem) {
var key = generateStrKey(elem);
if (!(key in mergedElems)) {
mergedElems[key] = {elem: elem, inArray2: false};
}
});
array2.forEach(function(elem) {
var key = generateStrKey(elem);
if (key in mergedElems) {
mergedElems[key].inArray2 = true;
}
});
Object.values(mergedElems).forEach(function(elem) {
if (elem.inArray2) {
result.push(elem.elem);
}
});
return result;
}
If there is a special case that cannot be solved, just by modifying the generateStrKey function, it could surely be solved. The trick of this function is that it uniquely represents each different data according to type and value.
This variant has some performance improvements. Avoid loops in case any array is empty. It also starts by walking through the shorter array first, so if it finds all the values of the first array in the second array, exits the loop.
function array_intersect(array1, array2)
{
var mergedElems = {},
result = [],
firstArray, secondArray,
firstN = 0,
secondN = 0;
function generateStrKey(elem) {
var typeOfElem = typeof elem;
if (typeOfElem === 'object') {
typeOfElem += Object.prototype.toString.call(elem);
}
return [typeOfElem, elem.toString(), JSON.stringify(elem)].join('__');
}
// Executes the loops only if both arrays have values
if (array1.length && array2.length)
{
// Begins with the shortest array to optimize the algorithm
if (array1.length < array2.length) {
firstArray = array1;
secondArray = array2;
} else {
firstArray = array2;
secondArray = array1;
}
firstArray.forEach(function(elem) {
var key = generateStrKey(elem);
if (!(key in mergedElems)) {
mergedElems[key] = {elem: elem, inArray2: false};
// Increases the counter of unique values in the first array
firstN++;
}
});
secondArray.some(function(elem) {
var key = generateStrKey(elem);
if (key in mergedElems) {
if (!mergedElems[key].inArray2) {
mergedElems[key].inArray2 = true;
// Increases the counter of matches
secondN++;
// If all elements of first array have coincidence, then exits the loop
return (secondN === firstN);
}
}
});
Object.values(mergedElems).forEach(function(elem) {
if (elem.inArray2) {
result.push(elem.elem);
}
});
}
return result;
}
Here is underscore.js implementation:
_.intersection = function(array) {
if (array == null) return [];
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = array.length; i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
Source: http://underscorejs.org/docs/underscore.html#section-62
Create an Object using one array and loop through the second array to check if the value exists as key.
function intersection(arr1, arr2) {
var myObj = {};
var myArr = [];
for (var i = 0, len = arr1.length; i < len; i += 1) {
if(myObj[arr1[i]]) {
myObj[arr1[i]] += 1;
} else {
myObj[arr1[i]] = 1;
}
}
for (var j = 0, len = arr2.length; j < len; j += 1) {
if(myObj[arr2[j]] && myArr.indexOf(arr2[j]) === -1) {
myArr.push(arr2[j]);
}
}
return myArr;
}
I think using an object internally can help with computations and could be performant too.
// Approach maintains a count of each element and works for negative elements too
function intersect(a,b){
const A = {};
a.forEach((v)=>{A[v] ? ++A[v] : A[v] = 1});
const B = {};
b.forEach((v)=>{B[v] ? ++B[v] : B[v] = 1});
const C = {};
Object.entries(A).map((x)=>C[x[0]] = Math.min(x[1],B[x[0]]))
return Object.entries(C).map((x)=>Array(x[1]).fill(Number(x[0]))).flat();
}
const x = [1,1,-1,-1,0,0,2,2];
const y = [2,0,1,1,1,1,0,-1,-1,-1];
const result = intersect(x,y);
console.log(result); // (7) [0, 0, 1, 1, 2, -1, -1]
I am using map even object could be used.
//find intersection of 2 arrs
const intersections = (arr1,arr2) => {
let arrf = arr1.concat(arr2)
let map = new Map();
let union = [];
for(let i=0; i<arrf.length; i++){
if(map.get(arrf[i])){
map.set(arrf[i],false);
}else{
map.set(arrf[i],true);
}
}
map.forEach((v,k)=>{if(!v){union.push(k);}})
return union;
}
This is a proposed standard: With the currently stage 2 proposal https://github.com/tc39/proposal-set-methods, you could use
mySet.intersection(mySet2);
Until then, you could use Immutable.js's Set, which inspired that proposal
Immutable.Set(mySet).intersect(mySet2)
I extended tarulen's answer to work with any number of arrays. It also should work with non-integer values.
function intersect() {
const last = arguments.length - 1;
var seen={};
var result=[];
for (var i = 0; i < last; i++) {
for (var j = 0; j < arguments[i].length; j++) {
if (seen[arguments[i][j]]) {
seen[arguments[i][j]] += 1;
}
else if (!i) {
seen[arguments[i][j]] = 1;
}
}
}
for (var i = 0; i < arguments[last].length; i++) {
if ( seen[arguments[last][i]] === last)
result.push(arguments[last][i]);
}
return result;
}
If your arrays are sorted, this should run in O(n), where n is min( a.length, b.length )
function intersect_1d( a, b ){
var out=[], ai=0, bi=0, acurr, bcurr, last=Number.MIN_SAFE_INTEGER;
while( ( acurr=a[ai] )!==undefined && ( bcurr=b[bi] )!==undefined ){
if( acurr < bcurr){
if( last===acurr ){
out.push( acurr );
}
last=acurr;
ai++;
}
else if( acurr > bcurr){
if( last===bcurr ){
out.push( bcurr );
}
last=bcurr;
bi++;
}
else {
out.push( acurr );
last=acurr;
ai++;
bi++;
}
}
return out;
}

JavaScript: How to match out-of-order arrays

I'm trying to work out how to match arrays that share the same elements, but not necessarily in the same order.
For example, these two arrays share the same set of elements, even though they're in a different order.
Is there any way to determine whether two arrays contain the same elements?
var search1 = ["barry", "beth", "debbie"];
var search2 = ["beth", "barry", "debbie"];
if (search1 == search2) {
document.write("We've found a match!");
} else {
document.write("Nothing matches");
}
I've got a Codepen of this running at the moment over here: http://codepen.io/realph/pen/grblI
The problem with some of the other solutions is that they are of O(n²) complexity, if they're using a for loop inside of a for loop. That's slow! You don't need to sort either—also slow.
We can speed this up to O(2n) complexity1 by using a simple dictionary. This adds O(2n) storage, but that hardly matters.
JavaScript
var isEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = true; // put each item in arr1 into the dictionary
})
for (; i < l; i++) { // run through the second array
if (!(arr2[i] in holder)) return false;
// if it's not in the dictionary, return false
}
return true; // otherwise, return true
}
}
Test Case
var arr1 = ["barry", "beth", "debbie"],
arr2 = ["beth", "barry", "debbie"];
console.log(isEqual(arr1,arr2));
// returns true
fiddle
Improvement
As Ahruss pointed out, the above function will return true for two arrays that are seemingly equal. For example, [1,1,2,3] and [1,2,2,3] would return true. To overcome this, simply use a counter in the dictionary. This works because !undefined and !0 both return true.
var isReallyEqual = function (arr1, arr2) {
if (arr1.length !== arr2.length) {
return false; // no point in wasting time if they are of different lengths
} else {
var holder = {}, i = 0, l = arr2.length;
// holder is our dictionary
arr1.forEach(function (d) {
holder[d] = (holder[d] || 0) + 1;
// checks whether holder[d] is in the dictionary: holder[d] || 0
// this basically forces a cast to 0 if holder[d] === undefined
// then increments the value
})
for (; i < l; i++) { // run through the second array
if (!holder[arr2[i]]) { // if it's not "in" the dictionary
return false; // return false
// this works because holder[arr2[i]] can be either
// undefined or 0 (or a number > 0)
// if it's not there at all, this will correctly return false
// if it's 0 and there should be another one
// (first array has the element twice, second array has it once)
// it will also return false
} else {
holder[arr2[i]] -= 1; // otherwise decrement the counter
}
}
return true;
// all good, so return true
}
}
Test Case
var arr1 = [1, 1, 2],
arr2 = [1, 2, 2];
isEqual(arr1, arr2); // returns true
isReallyEqual(arr1, arr2); // returns false;
1: It's really O(n+m) complexity, whereby n is the size of the first array and m of the second array. However, in theory, m === n, if the arrays are equal, or the difference is nominal as n -> ∞, so it can be said to be of O(2n) complexity. If you're feeling really pedantic, you can say it's of O(n), or linear, complexity.
you can use this function to compare two arrays
function getMatch(a, b) {
for ( var i = 0; i < a.length; i++ ) {
for ( var e = 0; e < b.length; e++ ) {
if ( a[i] === b[e] ){
return true;
}
}
}
}
Feed your arrays to the following function:
function isArrayEqual(firstArray, secondArray) {
if (firstArray === secondArray) return true;
if (firstArray == null || secondArray == null) return false;
if (firstArray.length != secondArray.length) return false;
// optional - sort the arrays
// firstArray.sort();
// secondArray.sort();
for (var i = 0; i < firstArray.length; ++i) {
if (firstArray[i] !== secondArray[i]) return false;
}
return true;
}
Now you may be thinking, can't I just say arrayOne.sort() and arrayTwo.sort() then compare if arrayOne == arrayTwo? The answer is no you can't in your case. While their contents may be the same, they're not the same object (comparison by reference).
You need to simply sort them, then compare them
function compareArrayItems(array1, array2){
array1 = array1.sort();
array2 = array2.sort();
return array1.equals(array2);
}
fiddle
You can use the equals function provided in How to compare arrays in JavaScript?
Sort them firstly. Secondly, if their length is different, then they're not a match.
After that, iterate one array and test a[i] with b[i], a being the first array, b the second.
var search1 = ["barry", "beth", "debbie"],
search2 = ["beth", "barry", "debbie"];
// If length are different, than we have no match.
if ((search1.length != search2.length) || (search1 == null || search2 == null))
document.write("Nothing matches");
var a = search1.sort(),
b = search2.sort(),
areEqual = true;
for (var i = 0; i < a.length; i++) {
// if any two values from the two arrays are different, than we have no match.
if (a[i] != b[i]) {
areEqual = false;
break; // no need to continue
}
}
document.write(areEqual ? "We've found a match!" : "Nothing matches");

How do I know an array contains all zero(0) in Javascript

I have an array. I need to generate an alert if all the array items are 0.
For example,
if myArray = [0,0,0,0];
then alert('all zero');
else
alert('all are not zero');
Thanks.
You can use either Array.prototype.every or Array.prototype.some.
Array.prototype.every
With every, you are going to check every array position and check it to be zero:
const arr = [0,0,0,0];
const isAllZero = arr.every(item => item === 0);
This has the advantage of being very clear and easy to understand, but it needs to iterate over the whole array to return the result.
Array.prototype.some
If, instead, we inverse the question, and we ask "does this array contain anything different than zero?" then we can use some:
const arr = [0,0,0,0];
const someIsNotZero = arr.some(item => item !== 0);
const isAllZero = !someIsNotZero; // <= this is your result
This has the advantage of not needing to check the whole array, since, as soon it finds a non-zero value, it will instantly return the result.
for loop
If you don't have access to modern JavaScript, you can use a for loop:
var isAllZero = true;
for(i = 0; i < myArray.length; ++i) {
if(myArray[i] !== 0) {
isAllZero = false;
break;
}
}
// `isAllZero` contains your result
RegExp
If you want a non-loop solution, based on the not-working one of #epascarello:
var arr = [0,0,0,"",0],
arrj = arr.join('');
if((/[^0]/).exec(arrj) || arr.length != arrj.length){
alert('all are not zero');
} else {
alert('all zero');
}
This will return "all zero" if the array contains only 0
Using ECMA5 every
function zeroTest(element) {
return element === 0;
}
var array = [0, 0, 0, 0];
var allZeros = array.every(zeroTest);
console.log(allZeros);
array = [0, 0, 0, 1];
allZeros = array.every(zeroTest);
console.log(allZeros);
Use an early return instead of 2, 3 jumps. This will reduce the complexity. Also we can avoid initialisation of a temp variable.
function ifAnyNonZero (array) {
for(var i = 0; i < array.length; ++i) {
if(array[i] !== 0) {
return true;
}
}
return false;
}
Using Math.max when you know for certain that no negative values will be present in the array:
const zeros = [0, 0, 0, 0];
Math.max(...zeros) === 0; // true
No need to loop, simple join and reg expression will work.
var arr = [0,0,0,10,0];
if((/[^0]/).exec(arr.join(""))){
console.log("non zero");
} else {
console.log("I am full of zeros!");
}
Another slow way of doing it, but just for fun.
var arr = [0,0,0,0,10,0,0];
var temp = arr.slice(0).sort();
var isAllZeros = temp[0]===0 && temp[temp.length-1]===0;
you can give a try to this :
var arr = [0,0,0,0,0];
arr = arr.filter(function(n) {return n;});
if(arr.length>0) console.log('Non Zero');
else console.log("All Zero");

fastest way to detect if duplicate entry exists in javascript array?

var arr = ['test0','test2','test0'];
Like the above,there are two identical entries with value "test0",how to check it most efficiently?
If you sort the array, the duplicates are next to each other so that they are easy to find:
arr.sort();
var last = arr[0];
for (var i=1; i<arr.length; i++) {
if (arr[i] == last) alert('Duplicate : '+last);
last = arr[i];
}
This will do the job on any array and is probably about as optimized as possible for handling the general case (finding a duplicate in any possible array). For more specific cases (e.g. arrays containing only strings) you could do better than this.
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
return true;
}
}
}
return false;
}
There are lots of answers here but not all of them "feel" nice... So I'll throw my hat in.
If you are using lodash:
function containsDuplicates(array) {
return _.uniq(array).length !== array.length;
}
If you can use ES6 Sets, it simply becomes:
function containsDuplicates(array) {
return array.length !== new Set(array).size
}
With vanilla javascript:
function containsDuplicates(array) {
return array
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
However, sometimes you may want to check if the items are duplicated on a certain field.
This is how I'd handle that:
containsDuplicates([{country: 'AU'}, {country: 'UK'}, {country: 'AU'}], 'country')
function containsDuplicates(array, attribute) {
return array
.map(function (item) { return item[attribute] })
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
Loop stops when found first duplicate:
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]]) {
return true;
}
x[arr[i]] = true;
}
return false;
}
Edit (fix 'toString' issue):
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]] === true) {
return true;
}
x[arr[i]] = true;
}
return false;
}
this will correct for case has_duplicates(['toString']); etc..
var index = myArray.indexOf(strElement);
if (index < 0) {
myArray.push(strElement);
console.log("Added Into Array" + strElement);
} else {
console.log("Already Exists at " + index);
}
You can convert the array to to a Set instance, then convert to an array and check if the length is same before and after the conversion.
const hasDuplicates = (array) => {
const arr = ['test0','test2','test0'];
const uniqueItems = new Set(array);
return array.length !== uniqueItems.size();
};
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test0'])}`);
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test3'])}`);
Sorting is O(n log n) and not O(n). Building a hash map is O(n). It costs more memory than an in-place sort but you asked for the "fastest." (I'm positive this can be optimized but it is optimal up to a constant factor.)
function hasDuplicate(arr) {
var hash = {};
var hasDuplicate = false;
arr.forEach(function(val) {
if (hash[val]) {
hasDuplicate = true;
return;
}
hash[val] = true;
});
return hasDuplicate;
}
It depends on the input array size. I've done some performance tests with Node.js performance hooks and found out that for really small arrays (1,000 to 10,000 entries) Set solution might be faster. But if your array is bigger (like 100,000 elements) plain Object (i. e. hash) solution becomes faster. Here's the code so you can try it out for yourself:
const { performance } = require('perf_hooks');
function objectSolution(nums) {
let testObj = {};
for (var i = 0; i < nums.length; i++) {
let aNum = nums[i];
if (testObj[aNum]) {
return true;
} else {
testObj[aNum] = true;
}
}
return false;
}
function setSolution(nums) {
let testSet = new Set(nums);
return testSet.size !== nums.length;
}
function sortSomeSolution(nums) {
return nums
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
function runTest(testFunction, testArray) {
console.log(' Running test:', testFunction.name);
let start = performance.now();
let result = testFunction(testArray);
let end = performance.now();
console.log(' Duration:', end - start, 'ms');
}
let arr = [];
let setSize = 100000;
for (var i = 0; i < setSize; i++) {
arr.push(i);
}
console.log('Set size:', setSize);
runTest(objectSolution, arr);
runTest(setSolution, arr);
runTest(sortSomeSolution, arr);
On my Lenovo IdeaPad with i3-8130U Node.js v. 16.6.2 gives me following results for the array of 1,000:
results for the array of 100,000:
Assuming all you want is to detect how many duplicates of 'test0' are in the array. I guess an easy way to do that is to use the join method to transform the array in a string, and then use the match method.
var arr= ['test0','test2','test0'];
var str = arr.join();
console.log(str) //"test0,test2,test0"
var duplicates = str.match(/test0/g);
var duplicateNumber = duplicates.length;
console.log(duplicateNumber); //2

Categories

Resources