-1

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!

3 Answers3

1

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);

Edit

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);
pilchard
  • 12,414
  • 5
  • 11
  • 23
0

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);
Fabrizio
  • 233
  • 1
  • 10
  • Hey @Fabrizio, thanks so much for the help man! Unfortunately the output from your code isn't matching the output I need – Brandon Bryan Dec 11 '22 at 13:21
0

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))
gog
  • 10,367
  • 2
  • 24
  • 38
  • why mutate the Object prototype? Especially for a beginner that is a terrible idea. – pilchard Dec 11 '22 at 13:53
  • @pilchard: what exactly is "terrible" about it? – gog Dec 11 '22 at 13:53
  • plenty of discussion already on SO (ie [Why is extending native objects a bad practice?](https://stackoverflow.com/questions/14034180/why-is-extending-native-objects-a-bad-practice)), but in general makes the code less transportable outside of a given codebase. Why not just declare a helper function rather than attaching it to the Object.prototype? – pilchard Dec 11 '22 at 13:56
  • A helper function wouldn't be chainable, which is the whole point. – gog Dec 11 '22 at 13:59