Transforming an array into an array of arrays

Viewed 142

I am trying to do an opposite of flattening an array.

I have the following input JSON array of 4 elements:

[
  {
    "nestedObj": {
      "id":12
    }
  },
  {
    "nestedObj": {
      "id":555
    }
  },
  {
    "nestedObj": {
      "id":555
    }
  },
  {
    "nestedObj" :{
      "id":771
    }
  }
]

I want to transform it to an array of arrays, where each subarray has elements of the same nestedObj.id grouped up together.

I can assume the initial JSON is sorted by nestedObj.id.

In the above example, the id of nestedObj of 2nd and 3rd element are the same (555), so those elements would be grouped into one sub-array.

This would be the result, an array of only 3 sub-array elements:

[
  [{
    "nestedObj": {
      "id":12
    }
  }],
  [{
    "nestedObj": {
      "id":555
    }
  },
  {
    "nestedObj": {
      "id":555
    }
  }],
  [{
    "nestedObj" :{
      "id":771
    }
  }]
]

And this is the code that gets me what I want:

const data = [ /* ...the above input data... */ ];
let result = [];
let prevId = null;
for (let elem of data) {
  let currId = elem.nestedObj.id;
  if (currId === prevId) {
    result[result.length - 1].push({...elem});
  } else {
    result.push([{...elem}]);
  }
  prevId = currId;
}

But as you can see... the code is very declarative. It's not very JavaScript-like, in a functional programming sense.

How can I re-write it using e.g. reduce or other 'modern JS' techniques?

3 Answers

Just group the objects.

let array = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }],
    result = Object.values(array.reduce((r, o) => {
        (r[o.nestedObj.id] = r[o.nestedObj.id] || []).push(o);
        return r;
    }, {}));

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

You can group by id using the function reduce, extract the grouped values using the function Object.values, and finally map the array to build the desired output.

This is assuming we have only one attribute called nestedObj

let arr = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }],
    result = Object.values(arr.reduce((a, {nestedObj: {id}}) => {
      (a[id] || (a[id] = [])).push(id);
      return a;
    }, {})).map(r => r.map(id => ({nestedObj: {id}})));

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

Use a Map to group the items with same id then get the final values from the Map

const data = [{ nestedObj: { id: 12 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 555 } }, { nestedObj: { id: 771 } }]

const map = new Map;

data.forEach(o => {
  const {nestedObj:{id}} = o;
  map.has(id) ? map.get(id).push(o) : map.set(id,[o]);
});

console.log([...map.values()])

Related