chrisbib
chrisbib

Reputation: 25

Grouping arrays into further sub arrays

Say I have an array of arrays like so:

const input = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd']]

I would like to arrange it so that they are futher sorted into groups of arrays, with the first element in each dictating how they are sorted. The result would look like this:

const output = [[[1, 'aaa'], [1, 'bbb']], [[2, 'ccc'], [2, 'ddd']]]

The first group would have all arrays where the first element is 1, the second group would all have 2 as the first element, and so forth.

The second solution to this question is probably the closest thing I can find: Grouping array group into separate sub array group

However, my modifications don't seem to work and I imagine I am misunderstand something about the functionality of .reduce(). What I've tried:

const input = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd']];

const output = input.reduce(
  (r, c, i, a) => (
    c[0] != a[i - 1][0] ? r.push([c]) : r[r.length - 1].push(c), r
  ), []);

Am I heading in the right direction, or is there a more simple solution that lies elsewhere?

Upvotes: 0

Views: 59

Answers (6)

Pedro Amaral Couto
Pedro Amaral Couto

Reputation: 2115

Simply put each array item into the correspondent array index:

const groups = [];
input.forEach(
  item => {
    groups[item[0]-1] ??= [];
    groups[item[0]-1].push(item);
  }
);

That approach might result in empty array items, if there are missing numbers. Furthermore, it assumes the lowest number is 1. If you don't like those constraints, you may use an object instead:

const groupsObject = {};
input.forEach(
  item => {
    groupsObject[item[0]] ??= [];
    groupsObject[item[0]].push(item);
  }
);
const groups = Object.values(groupsObject);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386604

Just group with the help of an object.

const
    data = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd']],
    result = Object.values(data.reduce((r, a) => {
        (r[a[0]] ??= []).push(a);
        return r;
    }, {}));

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Andy
Andy

Reputation: 63524

Use an object to hold this information. The key is the first element of the nested array; the value is (initially) an empty array that you can push those nested arrays into.

Use a simple loop to achieve this, and finally return just the Object.values.

const input = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd'], [1, 'ccc']];

function nest(arr) {
  const out = {};
  for (const [index, ...rest] of arr) {
    out[index] ??= [];
    out[index].push([ index, ...rest ]);
  }
  return Object.values(out);
}

console.log(JSON.stringify(nest(input)));

Additional documentation

Upvotes: 0

Insyte
Insyte

Reputation: 2236

Assuming that it's possible for an index to be skipped, 1,2...4,5 I have used filter here to keep the reduce function as simple as possible.

const input = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd'], [4, 'aaa'], [5, 'aaa']]

const output = input.reduce((acc, curr) => {
    const pos = curr[0] - 1
    if (!acc[pos]) acc[pos] = []
    return acc[pos].push(curr) && acc
}, []).filter(Boolean)

Upvotes: 0

alguerocode
alguerocode

Reputation: 132

this algorithms might be readable. i use curIndex variable to track how to push the arrays

let curIndex = 0;
const output = input.reduce((prev: any, cur) => {
  // if prev is empty
  if (prev.length == 0) prev.push([cur]);
  // if the number equal the cur at current index
  else if (prev[curIndex][0][0] == cur[0]) prev[curIndex].push(cur);
  // create new array
  else {
    prev.push([cur]);
    curIndex++;
  }

  return prev;
}, []);

I hope it will be helpful

Upvotes: 0

eroironico
eroironico

Reputation: 1372

So basically what i'm doing here (inside the .reduce()) is searching for a group that contains a value ([number, string]) which number (id) is equal to the number of the value i'm trying to add. If i find a group i push the new value (nv) inside that group, else i create a new group ([nv])

const input = [[1, 'aaa'], [1, 'bbb'], [2, 'ccc'], [2, 'ddd'], [4, 'aaa'], [5, 'aaa']]

const output = input.reduce((groups, nv) => {
    const group = groups.find(g => g.some(([id]) => id === nv[0]))
    
    if(!group) groups.push([nv])
    else group.push(nv)

    return groups
}, [])

// output: [[[1, 'aaa'], [1, 'bbb']], [[2, 'ccc'], [2, 'ddd']], [[4, 'aaa']], [[5, 'aaa']]]

Upvotes: 2

Related Questions