Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ungroup an object array based on multiple array properties in Javascript

Think of an object array, each of them having certain array properties like versions, targets. I want to ungroup objects for each version and target.

const myArray = [
  { 'name': 'a', versions: [1, 2], targets: ['server1', 'server2']},
  { 'name': 'b', versions: [], targets: ['server1', 'server2', 'server3']},
  { 'name': 'c', versions: [1], targets: []}
]

Desired output for myArray above would be:

[
  { 'name': 'a', version: 1,         target: 'server1'},
  { 'name': 'a', version: 1,         target: 'server2'},
  { 'name': 'a', version: 2,         target: 'server1'},
  { 'name': 'a', version: 2,         target: 'server2'},
  { 'name': 'b', version: undefined, target: 'server1'},
  { 'name': 'b', version: undefined, target: 'server2'},
  { 'name': 'b', version: undefined, target: 'server3'},
  { 'name': 'c', version: 1,         target: undefined},
]

I pondered using nested for/forEach loops, but am almost pretty sure there must be a more precise and reasonable way of achieving it built-in es6 functions or so. And that's what I'm asking for.

like image 962
vahdet Avatar asked Sep 05 '25 03:09

vahdet


1 Answers

You could use .flatMap:

  const notEmpty = arr => arr.length ? arr : [undefined];

  myArray.flatMap(({ name, versions, targets }) => notEmpty(versions).flatMap(version => notEmpty(targets).map(target => ({ name, version, target }))));

Or with more dimensions, generators get very useful:

  function* cartesian(obj, key, ...keys) {
     if(!key) {
         yield obj;
         return;
     }

    const { [key + "s"]: entries, ...rest } = obj;
    for(const entry of (entries.length ? entries : [undefined])) {
        yield* cartesian({ [key]: entry, ...rest }, ...keys);
     }
 }

 myArray.flatMap(it => cartesian(it, "version", "target"))
like image 130
Jonas Wilms Avatar answered Sep 08 '25 00:09

Jonas Wilms