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);
}
}
}
Related
This is my first array:
first_arr = [["кстати", 1], ["скажем", 1], ["блин", 1], ["реально", 1], ["вообще", 1], ["допустим", 1], ["фактически", 1], ["получается", 1]]
And the second:
second_arr = [['кстати', 1], ['скажем', 2], ['реально', 3], ['вообще', 3], ['ладно', 5]]
How can I summarize it to be like:
final_arr = [["кстати", 2], ["скажем", 3], ["блин", 1], ["реально", 4], ["вообще", 4], ["допустим", 1], ["фактически", 1], ["получается", 1], ['ладно', 5]]
You can concatenate the two arrays and reduce over it, incrementing the count of each key. Then, call Object.entries on the result:
first_arr = [["кстати", 1], ["скажем", 1], ["блин", 1], ["реально", 1], ["вообще", 1], ["допустим", 1], ["фактически", 1], ["получается", 1]]
second_arr = [['кстати', 1], ['скажем', 2], ['реально', 3], ['вообще', 3], ['ладно', 5]]
const res = Object.entries(first_arr.concat(second_arr).reduce((a, [key, val]) => (a[key] = (a[key] || 0) + val, a), {}))
console.log(res)
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
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I have multiple arrays in a main/parent array like this:
var arr = [
[1, 17],
[1, 17],
[1, 17],
[2, 12],
[5, 9],
[2, 12],
[6, 2],
[2, 12],
[2, 12]
];
I have the code to select the arrays that are repeated 3 or more times (> 3) and assign it to a variable.
The code is:
var arr = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]]
arr.sort((a, b) => a[0] - b[0] || a[1] - b[1])
// define equal for array
const equal = (arr1, arr2) => arr1.every((n, j) => n === arr2[j])
let GROUP_SIZE = 3
first = 0, last = 1, res = []
while(last < arr.length){
if (equal(arr[first], arr[last])) last++
else {
if (last - first >= GROUP_SIZE) res.push(arr[first])
first = last
}
}
if (last - first >= GROUP_SIZE) res.push(arr[first])
console.log(res)
So the final result is:
console.log(repeatedArrays);
>>> [[1, 17], [2, 12]]
My problem: But the problem is, I have an array like this {from: [12, 0], to: [14, 30]}.
var arr = [
[1, 17],
[1, 17],
[1, 17],
[2, 12],
[5, 9],
[2, 12],
[6, 2],
{from: [12, 0], to: [14, 5]},
{from: [12, 0], to: [14, 5]},
{from: [4, 30], to: [8, 20]},
{from: [12, 0], to: [14, 5]},
{from: [4, 30], to: [8, 20]},
[2, 12],
[2, 12]
];
When I try to use the above code, it doesn't work. The error message is:
Uncaught TypeError: arr1.every is not a function
The final result should be:
console.log(repeatedArrays);
>>> [[1, 17], [2, 12], {from: [12, 0], to: [14, 5]}]
How can I make that code above work?
If you introduce a non array into the mix, you need to handle it differently.
Yours already work with array so I'm adding object style check for both sort and equal.
var arr = [
[1, 17],
[1, 17],
[1, 17],
[2, 12],
[5, 9],
[2, 12],
[6, 2],
{ from: [4, 30], to: [8, 21] },
{ from: [12, 0], to: [14, 5] },
{ from: [12, 0], to: [14, 5] },
{ from: [4, 30], to: [8, 20] },
{ from: [12, 0], to: [14, 5] },
{ from: [4, 30], to: [8, 20] },
[2, 12],
[2, 12]
];
arr.sort((a, b) => {
if (a instanceof Array && b instanceof Array) {
return a[0] - b[0] || a[1] - b[1]
} else if (a instanceof Array || b instanceof Array) {
return a instanceof Array ? -1 : 1
} else {
return a.from[0] - b.from[0] || a.from[1] - b.from[1] || a.to[0] - b.to[0] || a.to[1] - b.to[1]
}
});
// define equal for array
const equal = (arr1, arr2) => {
if (arr1 instanceof Array) {
return arr1.every((n, j) => n === arr2[j]);
} else {
if (arr2 instanceof Array) return false;
for (let k in arr1) {
if (!arr1[k].every((n, j) => n === arr2[k][j])) {
return false
}
}
return true;
}
};
let GROUP_SIZE = 3;
(first = 0), (last = 1), (res = []);
while (last < arr.length) {
if (equal(arr[first], arr[last])) last++;
else {
if (last - first >= GROUP_SIZE) res.push(arr[first]);
first = last;
}
}
if (last - first >= GROUP_SIZE) res.push(arr[first]);
console.log(res);
You can use the function reduce for grouping and counting the objects and then execute the function filter for getting the object with count >= 3.
var array = [ [1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12], [2, 12] ];
let result = Object.values(array.reduce((a, [c, b]) => {
let key = `${c}|${b}`;
(a[key] || (a[key] = {count: 0, value: [c, b]})).count++;
return a;
}, {})).filter(o => {
if (o.count >= 3) {
delete o.count;
return true;
}
return false;
}).map(({value}) => value);
console.log(result);
.as-console-wrapper { min-height: 100%; }
Really simple - filter it all, then remove duplicates with Set and JSON methods (because it's nested arrays not objects):
var array = [
[1, 17],
[1, 17],
[1, 17],
[2, 12],
[5, 9],
[2, 12],
[6, 2],
[2, 12],
[2, 12]
];
var repeatedArrays = [...new Set(array.filter(e => array.filter(f => JSON.stringify(e.sort()) == JSON.stringify(f.sort()))).map(JSON.stringify))].map(JSON.parse);
console.log(repeatedArrays);
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}`)
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]]