Here is the array:
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20]
How can I achieve the following output?
output: // [[1, 1, 1, 1], [2, 2, 2], 4, 5, 10, [20, 20], 391, 392, 591]
Thanks for any help provided!
Here is the array:
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20]
How can I achieve the following output?
output: // [[1, 1, 1, 1], [2, 2, 2], 4, 5, 10, [20, 20], 391, 392, 591]
Thanks for any help provided!
You can use a basic 'group by' and then flatten any arrays in the result with length 1.
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const result = Object.values(arr
.reduce((a, c) => ((a[c] ??= []).push(c), a), {}))
.map(a => a.length > 1 ? a : a[0]);
console.log(result);
The sorting that is seen in the output is a result of of javascript object property ordering which sorts integer keys first in ascending order, followed by non-integer keys in insertion order, see: Does JavaScript guarantee object property order?.
If you don't want to rely on the implicit sorting provided by the Object you can explicitly sort()
the input before reducing it directly into an array. Here using a closure over a temporary 'group' array g
to hold a reference to the current grouping.
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const result = [...arr]
.sort((a, b) => a - b)
.reduce(
(g => (a, c, i, { [i - 1]: p }) => ((c !== p && a.push(g = [])), g.push(c), a))(),
[],
)
.map(a => a.length > 1 ? a : a[0]);
console.log(result);
The same logic as the above can be implemented in a more transparent way using a for...of loop with explicit checks for properties instead of using nullish coallescing assignment (??=)
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const grouped = {};
for (const element of arr) {
if (grouped[element] === undefined) {
grouped[element] = [];
}
grouped[element].push(element);
}
const result = Object.values(grouped)
.map(a => a.length > 1 ? a : a[0]);
console.log(result);
The second example can also be expanded to use a simple for...of
loop thus making the logic more transparent.
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const sorted = [...arr].sort((a, b) => a - b);
const grouped = [];
let group;
for (const [i, current] of sorted.entries()) {
const previous = sorted[i - 1];
if (current !== previous) {
group = [];
grouped.push(group);
}
group.push(current);
}
const result = grouped.map(a => a.length > 1 ? a : a[0]);
console.log(result);
This should work:
let array = [1, 1, 3, 2, 2, 5, 6, 4, 4];
array.sort((a, b) => a > b);
const result = [];
while(array.length > 0) {
const firstEl = array[0];
array.splice(0,1);
const current = [firstEl];
for(let x = 0; x < array.length; x++) {
if(array[x] == firstEl) {
current.push(array[x]);
array.splice(x, 1);
}
}
result.push(current);
}
console.log(result);
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20]
result = [
...arr
.sort((x, y) => x - y)
.reduce(
(m, x) => m.set(x, (m.get(x) ?? 0) + 1),
new Map
)
].map(
([x, n]) => n === 1 ? x : Array(n).fill(x))
console.log(...result)
This produces what you're looking for, however that unpacking looks really ugly. How about a small utility function?
Object.prototype.tap = function (fn, ...args) {
return fn(this, ...args)
}
//
result = arr
.sort((x, y) => x - y)
.reduce(
(m, x) => m.set(x, (m.get(x) ?? 0) + 1),
new Map
)
.tap(Array.from)
.map(
([x, n]) => n === 1 ? x : Array(n).fill(x))