Reputation: 865
I need to generate a multidimensional array from a multidimensional array.
For example, my array:
var codes = [
['2', '12521', '3'],
['3', '32344', '2'],
['3', '35213', '2'],
['4', '42312', '2'],
['4', '41122', '1'],
['5', '51111', '1']
];
And I need to group the array based on element: For example grouping based on 1st element:
[0] => Array
(
[0] => Array
(
[0] => '2'
[1] => '12521'
}
)
[1] => Array
(
[0] => Array
(
[0] => '3'
[1] => '32344'
[2] => '3'
}
[1] => Array
(
[0] => '3'
[1] => '35213'
[2] => '2'
}
)
[2] => Array
(
[0] => Array
(
[0] => '4'
[1] => '42312'
[2] => '2'
}
[1] => Array
(
[0] => '4'
[1] => '41122'
[2] => '1'
}
)
...
var codes = [
['2', '12521', '3'],
['3', '32344', '2'],
['3', '35213', '2'],
['4', '42312', '2'],
['4', '41122', '1'],
['5', '51111', '1']
];
const output = codes.reduce(({result, current}, [x, ...y]) => {
if (current !== x) result.push([]);
result[result.length - 1].push([x, ...y]);
return {result, current: x};
}, {result: []}).result;
console.log(output);
For the function , i can group based on the 1st element. But what if I need to group based on 2nd , 3rd element, any ways to do it without switching the position of the array elements (for example, not switching 3rd element to the 1st position and run the function)
Upvotes: 1
Views: 111
Reputation: 386883
You could specify an index and check the previous element for getting a new group.
var codes = [['2', '12521', '3'], ['3', '32344', '2'], ['3', '35213', '2'], ['3', '32344', '2'], ['3', '35213', '2'], ['3', '32344', '2'], ['3', '35213', '2'], ['4', '42312', '2'], ['4', '41122', '1'], ['5', '51111', '1']],
index = 0,
output = codes.reduce((result, data, i, array) => {
if (!array[i - 1] || data[index] !== array[i - 1][index] || result[result.length - 1].length === 5) result.push([]);
result[result.length - 1].push(data);
return result;
}, []);
console.log(output);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation: 11915
Instead of hard-coding it to group by the first element of the inner arrays using destructuring – which is positional – [x, ...y]
, you can leave the arrays untouched and instead use a parameter to select the index you want to group by.
//edit: this is the same answer as Nina, who replied faster
I recommend to wrap this in a function:
const codes = [
['2', '12521', '3'],
['3', '32344', '2'],
['3', '35213', '2'],
['4', '42312', '2'],
['3', '32344', '2'],
['3', '35213', '2'],
['3', '35213', '2'],
['4', '41122', '1'],
['5', '51111', '1']
];
const group_adjacent = (array, index) =>
array.reduce(({result, current}, elems) => {
if (current !== elems[index]) result.push([]);
result[result.length - 1].push(elems);
return {result, current: elems[index]};
}, {result: []}).result;
const output = group_adjacent(codes, 2); // 3rd column
console.log(output);
Note that this is not a general purpose grouping function! It only groups adjacent arrays where the element at index has the same value as the previous sibling.
// if grouped by index 2,
// the 1st and 3rd array won't end up in the same group!
const codes = [
['4', '41122', '1'],
['4', '42312', '2'],
['5', '51111', '1']
];
So make sure that the input has the column you group by sorted if you want a single group per value.
If you want to define a maximum number of arrays per group, you can introduce another parameter and start the next group if the current group has that many elements already. I also added sorting as an exercise (using a copy of the input array):
const codes = [
['2', '12521', '3'],
['3', '32344', '2'],
['3', '35213', '2'],
['4', '42312', '2'],
['3', '65432', '2'],
['3', '65656', '2'],
['4', '41122', '1'],
['3', '56565', '2'],
['3', '55665', '2'],
['5', '51111', '1']
];
const group_by = (array, index, max) => {
max = max || Infinity;
array = [...array].sort((a, b) => b[index] - a[index]);
return array.reduce(({result, current}, elems) => {
if (current !== elems[index] || result[result.length - 1].length >= max) result.push([]);
result[result.length - 1].push(elems);
return {result, current: elems[index]};
}, {result: []}).result;
}
const output = group_by(codes, 2, 5); // up to 5 per group
console.log(output);
Final remark: there are no safe guards against malformed input data. If one of the codes misses the value you group by – like ['2', '12521', /* missing */ ]
– then you get some probably undesired output.
Upvotes: 2
Reputation: 38209
You can group items by reduce
method and then just use desired items from grouped:
var codes = [
['2', '12521', '3'],
['3', '32344', '2'],
['3', '35213', '2'],
['4', '42312', '2'],
['4', '41122', '1'],
['5', '51111', '1']
];
let grouped = codes.reduce((a, [el1, el2, el3]) => {
a[el1] = a[el1] || {data: []};
a[el1].data.push([el1, el2, el3]);
return a;
}, {});
grouped = Object.values(grouped);
const result = grouped.map(s => s.data);
console.log(result);
Upvotes: 0