11

I've got two arrays in Javascript which currently look like this, but are updated by HTTP requests (node):

var x = [[292,"2349","902103","9"],[3289,"93829","092","920238"]] var y = [[292,"2349","902103","9"],[322,"93829","092","920238"],[924,"9320","8932","4329"]] 

I'm looking to compare these arrays, so that, if there is an array inside y that is not in x, it will be saved to a new array - z. Note that sometimes the order of arrays inside the arrays will change, but I would not like this to affect the result.

If there is an array inside x that is not in y, however, is should not be saved to z.

I read JavaScript array difference and have been able to replicate this, but if the x array is not shown in y, it is printed to z. I am wondering if it is possible for this not to be stored, only the different items in y?

8
  • Will the inner arrays always contain strings & numbers ? Commented Apr 9, 2016 at 17:33
  • They will always be in this format (always in the same order): [int,"str","str","str"]. Commented Apr 9, 2016 at 17:34
  • so the ints need to be different? is that enough ? Commented Apr 9, 2016 at 17:35
  • 1
    Possible duplicate of How to compare arrays in JavaScript? Commented Apr 9, 2016 at 17:54
  • 1
    This is NOT a duplicate. The How to compare arrays in JavaScript? question asks about comparing arrays which contains only primitives, like numbers or strings, whereas this question asks about comparing arrays which contain other arrays. Commented Apr 9, 2016 at 18:49

6 Answers 6

9

Use a higher-order function that accepts an array (which changes with each iteration of y) and returns a new function that operates on each element (nested array) in some. It returns true if the arrays contain the same elements regardless of order.

function matches(outer) { return function (el) { if (outer.length !== el.length) return false; return el.every(function (x) { return outer.indexOf(x) > -1; }); } } 

Iterate over y and return a list of arrays that aren't in x.

function finder(x, y) { return y.filter(function (el) { return !x.some(matches(el)); }); } finder(x, y); 

DEMO

Sign up to request clarification or add additional context in comments.

7 Comments

Is the .reduce in finder any different from a .filter?
Not really in this case. I added an edit to my answer.
This might not affect the OP's use case, but your matches function will return true for two subarrays that have items in different order: matches([1, '2', '3'])(['3', 1, '2']) === true. It will also return true if the first array has extra items in it: matches([1, 4, '2', '3', 5, '6'])(['3', 1, '2']) === true.
Not worried about the first point as the OP clarified that in his comments, but you're right about your second point and I've updated my code to reflect that, cheers @NoahFreitas.
You can also use Array.prototype.includes() instead of .indexOf() to determine whether an array contains specific element.
|
3

You can use this function arrayDiff.

It takes two arrays (A and B) and returns an array of all elements that are in the first array and not in the second (A \ B), with any duplicates removed. Two array elements are equal if their JSON serialization is the same.

var x = [[292,"2349","902103","9"],[3289,"93829","092","920238"]]; var y = [[292,"2349","902103","9"],[322,"93829","092","920238"],[924,"9320","8932","4329"]]; var z = arrayDiff(y, x); // z is [[322,"93829","092","920238"],[924,"9320","8932","4329"]] // arrayDiff :: [a], [a] -> [a] function arrayDiff(a1, a2) { let a1Set = toStringSet(a1), a2Set = toStringSet(a2); return Array.from(a1Set) .filter(jsonStr => !a2Set.has(jsonStr)) .map(JSON.parse); // toStringSet :: [a] -> Set<String> function toStringSet(arr) { return new Set(arr.map(JSON.stringify)); } } 

Comments

2

This should work even if the order in the inner arrays is different.
I'm assuming you will have only numbers and strings in there and you don't expect a strict comparison between them.

var x = [[292,"2349","902103","9"],[3289,"93829","092","920238"]]; var y = [[292,"2349","902103","9"],[322,"93829","092","920238"],[924,"9320","8932","4329"]]; // this will do y \ x var z = arrDiff(y, x); console.log(z); function arrDiff(arr1, arr2) { var rez = []; for (var i = 0; i < arr1.length; i++) { if ( ! contains(arr2, arr1[i])) { rez.push(arr1[i]); } } return rez; } function contains(arr, x) { x = x.slice().sort().toString(); for (var i = 0; i < arr.length; i++) { // compare current item with the one we are searching for if (x === arr[i].slice().sort().toString()) { return true; } } return false; } 

Comments

1

Try this:

function getArraysDiff(arr1, arr2) { var x = arr1.map(function(a) { return a.join("") }); var y = arr2.map(function(a) { return a.join("") }); var z = []; for ( var i = 0, l = arr1.length; i < l; i++ ) { if ( y.indexOf(x[i]) == -1 ) { z.push(arr1[i]) } } return z; } 

Or this:

x.filter((function(y) { return function(x) { return y.indexOf(x.join("")) > -1; } }( y.map(function(y) { return y.join("") }) ))) 

8 Comments

What if the sort order of internal arrays cannot be guaranteed to be same on both sides?
@GregW Can you clarify whether the order is guaranteed?
@fahadash I guess he could do a .sort() before the join
x = [[292, 2349]], y = [[2922, 349]] are considered equal here... using join is dirty.
They're not - that's the point - but your algorithm will assume they are because the inner arrays when joined both evaluate to "2922349".
|
1

You can use Array.prototype.forEach(), Array.prototype.every(), Array.prototype.map(), Array.prototype.indexOf(), JSON.stringify(), JSON.parse()

var z = []; y.forEach(function(val, key) { var curr = JSON.stringify(val); var match = x.every(function(v, k) { return JSON.stringify(v) !== curr }); if (match && z.indexOf(curr) == -1) z.push(curr) }); z = z.map(JSON.parse); 

var x = [ [292, "2349", "902103", "9"], [3289, "93829", "092", "920238"] ]; var y = [ [292, "2349", "902103", "9"], [322, "93829", "092", "920238"], [924, "9320", "8932", "4329"] ]; var z = []; y.forEach(function(val, key) { var curr = JSON.stringify(val); var match = x.every(function(v, k) { return JSON.stringify(v) !== curr }); if (match && z.indexOf(curr) == -1) z.push(curr) }); z = z.map(JSON.parse); console.log(z); document.querySelector("pre").textContent = JSON.stringify(z, null, 2)
<pre></pre>

Comments

1

You have got 2 arrays:

var x = [[292,"2349","902103","9"],[3289,"93829","092","920238"]]; var y = [[292,"2349","902103","9"],[322,"93829","092","920238"],[924,"9320","8932","4329"]]; 

To create the Z array, you need the following function:

function createZ(){ var i,j,k=0,z=[],p=x; for(j=0;j<y.length;j++){ for(i=0;i<p.length;i++){ if(y[j][0]===p[i][0] && y[j][1]===p[i][1] && y[j][2]===p[i][2] && y[j][3]===p[i][3]){ p.splice(i,1); break; } else { z[k++]=y[j]; console.log((y[j][0]===p[i][0])+" "+i+","+j); } } } return z; } 

Note that the createZ() also prints out the i,j of corresponding entry to the console.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.