How to find differences between two arrays with nested arrays - javascript

How to find differences between two arrays which have arrays nested inside?
I tried different approaches including filter but not succeeded.
We have two arrays:
var arr1 = [ [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6]];
var arr2 = [ [1, 1], [1, 2], [1, 5]];
All I need is to get an array with elements which do not exist in the first array as a result:
[[1, 3], [1, 4], [1, 6]]

You can filter on arr1 with the predicate there isn't some element in arr2 where all items match.
var arr1 = [ [1, 1], [1, 2, 3], [1, 3], [1, 4], [1, 5], [1, 6]];
var arr2 = [ [1, 1], [1, 2], [1, 5]];
let filtered = arr1.filter(a =>
!arr2.some(a2 => a.length === a2.length && a2.every((n, i) => n === a[i] ))
)
console.log(filtered)
You can make this more efficient at the expense of additional space if your lists are long enough to warrant it.

Related

How to group two-dimensional array by value at zero index

Let's say I have the following two-dimensional array:
const array = [[1, 1], [1, 2], [2, 1], [2, 2]];
What I want to do is to find all the values under first index which have common zero index value. So the result should be the following:
[[1, [1, 2]], [2, [1, 2]]]
Or maybe Map would look better:
[Map(1 => [1, 2]), Map(2 => [1, 2])]
How can I do that? With or without lodash. My solution looks a bit bloated and messy:
const array = [[1, 1], [1, 2], [2, 1], [2, 2]];
const grouppedCartItemsMap = new Map();
array.forEach((item) => {
const [locationId, contractId] = item;
if (!grouppedCartItemsMap.has(locationId)) {
grouppedCartItemsMap.set(locationId, [contractId]);
} else {
const existingItem = grouppedCartItemsMap.get(locationId);
if (!existingItem.includes(contractId)) {
grouppedCartItemsMap.set(locationId, [...existingItem, contractId]);
}
}
});
console.log(grouppedCartItemsMap);
If an object would work:
const array = [[1, 1], [1, 2], [2, 1], [2, 2]];
const grouped = {};
for (const [first, last] of array) {
grouped[first] ??= [];
grouped[first].push(last);
}
console.log(grouped);
This looks like a use case for reduce:
const array = [[1, 1], [1, 2], [2, 1], [2, 2]];
const grouppedCartItemsMap = array.reduce((acc, [locationId, contractId]) => {
if (!acc.has(locationId)) acc.set(locationId, [contractId]);
else acc.get(locationId).push(contractId);
return acc;
}, new Map());
console.log([...grouppedCartItemsMap.entries()]);

Find index of nested array

The code below should work but it doesn't. Does anyone know why?
const items = [
["bob", 5],
["jeff", 2],
["wal-E", 2],
["bob", 1],
["bob", 10]
];
items.indexOf(["bob", 5]);
//=> -1
It does not because indexOf uses simple comparison when looking for a match and [] !== [] because arrays are compared by reference, not by their contents. Try typing [5]===[5] it will give you false.
So you will need to manually write comparison logic using findIndex.
let items = [
["bob", 5],
["jeff", 2],
["wal-E", 2],
["bob", 1],
["bob", 10]
];
console.log(items.findIndex(x => x[0] === "bob" && x[1] === 5))
Array#indexOf uses strict equality === for comparison. What you're wanting to do can only work if you hold a reference to the same array:
const x = [1, 2];
[[0, 1], [1, 2], [2, 3]].indexOf(x);
//=> -1
[[0, 1], x, [2, 3]].indexOf(x);
//=> 1
What you can do is use Array#findIndex and compare each element of each nested array with each element of x at the same index:
const indexOf = (xs, ys) => ys.findIndex(yy => {
if (xs.length != yy.length) return false;
return yy.reduce((b, y, i) => b && Object.is(y, xs[i]), true);
});
indexOf([], [[1],[],[2, 3]]);
//=> 1
indexOf([1, 2], [[1, 2, 3, 4], [1, 2, 3], [1, 2]]);
//=> 2
indexOf([1, 2], [[2, 3], [3, 4]]);
//=> -1
indexOf([9, 9, 9], [[9], [9, 9], [9, 9, 9]]);
//=> 2
indexOf([9, NaN, 9], [[9], [9, NaN, 9], [9, 9]]);
//=> 1

How do I check if elements exist in an array using .some()?

I am trying to see if a particular pair of numbers exist in an array. I know that .some() works effectively when it's a simple array, but I can't seem to get it to work in nested arrays.
Thank you in advance.
const array = [[1, 0], [2, 0], [3, 1], [4, 3], [5, 2]];
// checks whether an element exists
const exists = (i) => i == [3, 1] || [1, 3];
console.log(array.some(exists));
// expected output: true
You need to check every value because of different arrays, you have different object references, and you misused the logical OR operator. This does not include a comparison with another value.
const array = [[1, 0], [2, 0], [3, 1], [4, 3], [5, 2]];
const exists = ([a, b]) => a === 3 && b === 1 || a === 1 && b === 3;
console.log(array.some(exists));
Here is a general solution.
const array = [[1, 0], [2, 0], [3, 1], [4, 3], [5, 2]];
const expected = [[1, 3], [3, 1]];
const exists = (i) => expected.some((j) => {
return i.every((k, n) => j[n] === k);
});
console.log(array.some(exists));
This could be wrapped in a function to make it reusable.
const array = [[1, 0], [2, 0], [3, 1], [4, 3], [5, 2]];
const expected = [[1, 3], [3, 1]];
const containsAny = (array, expected) => {
return array.some((i) => expected.some((j) => {
return i.every((k, n) => j[n] === k);
}))
};
console.log(containsAny(array, expected));
You could make a custom function out of it which makes it reusable.
This is a simple example. By flattening the array you can check if values are the same.
Got my inspiration on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
const array = [[1, 0], [2, 0], [3, 1], [4, 3], [5, 2]];
const checkArrayFor = (needle, haystack) => {
const flattenNeedle = needle.join(':');
const flattenArray = haystack.map(item => item.join(':'));
return flattenArray.includes(flattenNeedle);
};
console.log("Check for [1, 0]", checkArrayFor([1, 0], array));
console.log("Check for [1, 1]", checkArrayFor([1, 1], array));

.some() function not working with nested arrays?

For example, if I want to check whether arr1 (an array of 3 arrays) contains any element of arr2 (an array of 2 arrays), I use the .some() function, but it returns false.
let arr1 = [[1, 2], [2, 3], [3, 4]]
let arr2 = [[1, 2], [5, 2],]
if (arr1.some(x => arr2.includes(x))) {
alert('arr1 has arr2')
};
x does return the array [1, 2], but for some reason it doesn't pass the check for .some() in arr1.
.includes will return true if the passed item is === to one in the array, but separate objects are never === to each other in JS unless one was created from a reference from the other:
const arr = [1, 2];
console.log(arr === [1, 2]);
const arr2 = arr; // same reference
console.log(arr === arr2);
. I suppose one method would be to stringify the subarrays first:
let arr1 = [[1, 2], [2, 3], [3, 4]]
let arr2 = [[1, 2], [5, 2],]
if (arr1.some((subarr1) => {
const str1 = JSON.stringify(subarr1);
return arr2.some(subarr2 => JSON.stringify(subarr2) === str1);
})) {
console.log('arr1 has arr2')
}
I think includes function isn't working for arrays 2 dimension
Please change code like this
let arr1 = [[1, 2], [2, 3], [3, 4]]
let arr2 = [[1, 2], [5, 2],]
if (arr1.some(x => {
return arr2.some(item => item.toString() === x.toString())
})) {
alert('arr1 has arr2')
};
You can you _.isEqualof lodash to compare the Array.
let arr1 = [[1, 2], [2, 3], [3, 4]]
let arr2 = [[1, 2], [5, 2]]
if (arr1.some(x => {
return arr2.some(item => _.isEqual(item, x))
})) {
console.log('arr1 has arr2')
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Comparing arrays in arrays

I'm looking for a way to make comparisons between arrays in arrays.
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
For example, I would like to be able to find out how many of the arrays in small are found in large. Some function that, given the arrays above as arguments, would return 2, since [2, 2] and [3, 0] from small are found in large.
How would you go about doing that?
You can convert one of the arrays into a Set of hashes, and than filter the 2nd array using the set:
const small = [[1, 3], [2, 2], [2, 3], [3, 0]];
const large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
const containsCount = (arr1, arr2, hashFn) => {
const arr1Hash = new Set(arr1.map(hashFn));
return arr2.filter(s => arr1Hash.has(hashFn(s))).length;
}
const result = containsCount(small, large, ([a, b]) => `${a}-${b}`);
console.log(result);
You can try something like:
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
let z = zeta(small, large);
console.log(z);
function zeta(a, b) {
let join = m => m.join();
let x = a.map(join);
let y = b.map(join);
return x.reduce((n, m) => (y.indexOf(m)>0) ? ++n : n, 0);
}
I hope this helps!
Use every and some to compare the arrays with each other.
If you want to get an array containing the subarrays that match, use filter:
let result = small.filter(arr =>
large.some(otherArr =>
otherArr.length === arr.length && otherArr.every((item, i) => item === arr[i])
)
);
Which filters the subarray from small that some subarray from large has the same length and the same elements/items.
Demo:
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
let result = small.filter(arr =>
large.some(otherArr =>
otherArr.length === arr.length && otherArr.every((item, i) => item === arr[i])
)
);
console.log(result);
And if you want just a count, then use reduce instead of filter to count the mathched items (this makes use of the fact that the numeric value of true is 1 and that of false is 0):
let count = small.reduce((counter, arr) =>
counter + large.some(otherArr =>
otherArr.length === arr.length && otherArr.every((item, i) => item === arr[i])
)
, 0);
Demo:
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
let count = small.reduce((counter, arr) =>
counter + large.some(otherArr =>
otherArr.length === arr.length && otherArr.every((item, i) => item === arr[i])
)
, 0);
console.log(count);
Note: If the subarrays contain only numbers, the code could be simplified to use Array#toString instead of every and length comparaison:
let result = small.filter(arr => large.some(otherArr => "" + otherArr === "" + arr));
Which casts both arrays into strings and compares the two strings instead. This can be used with the reduce as well.
Create two nesting map() function oute for small and inner for large then use JSON.stringify()
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
var same=[];
small.map(function(element){
large.map(function(element2){
if(JSON.stringify(element)==JSON.stringify(element2)){
same.push(element);
}
});
});
console.log(same);
let small = [[1, 3], [2, 2], [2, 3], [3, 0]];
let large = [[1, 0], [1, 1], [2, 0], [2, 2], [2, 5], [3, 0], [3, 2]];
let largeArrayStringForm = large.map(item => item.toString())
let matchingItems = small.filter(item => largeArrayStringForm.includes(item.toString()))
console.log(`matchCount: ${matchingItems.length}`)

Categories