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}`)
Related
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()]);
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
I have a table like this:
The colored blocks have coordinates as an array:
[
[1, 1],
[2, 1],
[2, 4],
[2, 5],
[3, 2],
[3, 4],
[4, 4],
[4, 5],
[4, 6],
[4, 7],
[5, 3],
[6, 3],
[6, 4],
[6, 5]
]
Now I want to group the neighboring blocks (horizontal and vertical) to independent child array.
The output estimated is like:
[
[
[1, 1],
[2, 1]
],
[
[2, 4],
[2, 5],
[3, 4],
[4, 4],
[4, 5],
[4, 6],
[4, 7]
],
[
[3, 2]
],
[
[5, 3],
[6, 3],
[6, 4],
[6, 5]
]
]
How to use a function to do this?
Edit: I tried to iterate each value in the input array and compare to [1, 1], if one of the coordinate is the same, push them to an new array and delete in the input array, and use recursion to do this again. But I stuck by a problem...as I should group [2, 5] and [4, 5] but cannot group [4, 4] and [6, 4]
You could filter the grouped items and build a new group with the own indices.
const
data = [[1, 1], [2, 1], [2, 4], [2, 5], [3, 2], [3, 4], [4, 4], [4, 5], [4, 6], [4, 7], [5, 3], [6, 3], [6, 4], [6, 5]],
offsets = [[-1, 0], [1, 0], [0, -1], [0, 1]],
groups = data.reduce((r, [i, j]) => {
const
own = [];
temp = r.filter(group => {
const found = group.some(g => offsets.some(o => i + o[0] === g[0] && j + o[1] === g[1]));
if (!found) return true;
own.push(...group);
});
return [...temp, [...own, [i, j]]];
}, []);
console.log(groups);
.as-console-wrapper { max-height: 100% !important; top: 0; }
It seems to me that you are having just a logic problem, not a JavaScript, since you know how to nest the array, is just having a problem to find the right logic to group neighbors.
You can't just compare one part of the coordinate, since the other can me really distant. You need to compare both. If you want to find the neighbor coordinate you need to have one part of the coordinate equal and the another part exactly with 1 distance away.
var arr =
[
[1, 1],
[2, 1],
[2, 4],
[2, 5],
[3, 2],
[3, 4],
[4, 4],
[4, 5],
[4, 6],
[4, 7],
[5, 3],
[6, 3],
[6, 4],
[6, 5]
];
var groupedArr = new Array();
for(i = 0; i < arr.length; i++)
{
var grouped = false;
for(j = 0; j < groupedArr.length; j++)
{
for(k = 0; k < groupedArr[j].length; k++)
{
if((arr[i][0] == groupedArr[j][k][0] && Math.abs(arr[i][1] - groupedArr[j][k][1]) == 1)
|| (arr[i][1] == groupedArr[j][k][1] && Math.abs(arr[i][0] - groupedArr[j][k][0]) == 1))
{
groupedArr[j].push(arr[i]);
grouped = true;
break;
}
}
if(grouped)
break;
}
if(!grouped)
{
var newGroup = new Array();
newGroup.push(arr[i]);
groupedArr.push(newGroup);
}
}
console.log(groupedArr);
looks like clustering problem. But we can solve with this simple code for small data.
ax = [[1, 1], [2, 1], [2, 4], [2, 5], [3, 2], [3, 4], [4, 4], [4, 5], [4, 6], [4, 7], [5, 3], [6, 3], [6, 4], [6, 5]];
console.log(JSON.stringify(reForm(ax)));
function reForm(a) {
const ret = [];
while (a.length > 0) {
var sub = [a.shift()];
var i=0;
while (i<sub.length){
var j=0
while(j<a.length){
if((a[j][0]==sub[i][0] && Math.abs(a[j][1]-sub[i][1])==1) ||
(a[j][1]==sub[i][1] && Math.abs(a[j][0]-sub[i][0])==1))
sub.push(...a.splice(j,1));
else j++
}
i++;
}
ret.push(sub);
}
return ret;
}
You can do this by using recursion
var array =[
[1, 1],
[2, 1],
[2, 4],
[2, 5],
[3, 2],
[3, 4],
[4, 4],
[4, 5],
[4, 6],
[4, 7],
[5, 3],
[6, 3],
[6, 4],
[6, 5]
]
var returnval = new Array();
while(array.length>0)
{
var temp = new Array();
var item = array[0];
array.splice(0, 1);
temp.push(item);
findnext(item,temp);
returnval.push(temp);
}
console.log(returnval);
function findnext(item, temp)
{
for(var i=0;i<array.length;i++)
{
if((array[i][0]==item[0] && Math.abs(array[i][1]-item[1])==1) || (array[i][1]==item[1] && Math.abs(array[i][0]-item[0])==1))
{
temp.push(array[i]);
item1 = array[i];
array.splice(i,1);
findnext(item1, temp);
}
}
}
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));
Definition:Creates an array of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on.
Current Solution:
const zip = (...arr) => {
let maxLength = 0
let res = []
for (let el of arr) {
maxLength = Math.max(maxLength, el.length)
}
for (let j = 0; j < maxLength; j++) {
const foo = []
for (let n of arr) {
foo.push(n[j])
}
res.push(foo)
}
return res
}
Test Case:
test(('zip', () => {
expect(zip([1, 2], [4, 5], [9, 1])).toEqual([[1, 4, 9], [2, 5, 1]])
}
test('zip', () => {
expect(zip([1, 2, 3], [4, 5, 6])).toEqual([[1, 4], [2, 5], [3, 6]])
})
test('zip', () => {
expect(zip([1, 2], [], [3, 4, 5])).toEqual([
[1, undefined, 3],
[2, undefined, 4],
[undefined, undefined, 5],
])
})
I want to get a better way to achieve zip, current solution is ugly
See Destructuring Assignment and Array.prototype.map for more info.
// Proof.
const zip = (...args) => [...new Array(Math.max(...args.map(arr => arr.length)))].map((x, i) => args.map((y) => y[i]))
// Proof.
console.log(zip([1, 2], [4, 5], [9, 1])) // [[1, 4, 9], [2, 5, 1]]
console.log(zip([1, 2, 3], [4, 5, 6])) // [[1, 4], [2, 5], [3, 6]]
console.log(zip([1, 2], [], [3, 4, 5])) // [[1, undefined, 3], [2, undefined, 4], [undefined, undefined, 5]]