3

I have an object that contains other objects, such as:

let foo = {
  a: {
    b: {},
    c: {}
  },
  d: {
    e: {}
  }
};

Now I want to transform this into an array of objects, where the keys of the first two levels form a key / value pair, such as:

let transformedFoo = [
  { outer: 'a', inner: 'b' },
  { outer: 'a', inner: 'c' },
  { outer: 'd', inner: 'e' }
];

My current approach looks like this:

let fooTransformed = [];

Object.keys(foo).forEach(function (outerKey) {
  Object.keys(foo[outerKey]).forEach(function (innerKey) {
    fooTransformed.push({
      outer: outerKey,
      inner: innerKey
    });
  });
});

It works, but I think it's not "nice" (i.e., it's not nice to have two nested loops). Is there a better way on how to achieve this (I could imagine that there is a quite elegant purely functional solution, but I can't think of any)?

Golo Roden
  • 112,924
  • 78
  • 260
  • 376
  • 2
    [it seems](http://stackoverflow.com/questions/14810506/map-for-objects-instead-of-arrays) that your approach is the accepted one. – PM 77-1 Jan 27 '15 at 20:44
  • 2
    Logically, nested manipulation would require another nested loop (or conditional). A "functional" approach would just hide this fact from view. – David Pullar Jan 27 '15 at 20:46
  • 1
    @DavidPullar is right, instead of a nested loop, you'll have a nested map, plus a reduce to flatten out the results. See [below](http://stackoverflow.com/a/28179747/46871) – tlehman Jan 27 '15 at 21:02

1 Answers1

2

Using map and reduce:

> Object.keys(foo).map(function(key) { 
      return Object.keys(foo[key]).map(function(val) {
          return {outer: key, inner: val} } ) 
      }).reduce(function(a,b) { return a.concat(b) })

[ { outer: 'a', inner: 'b' },
  { outer: 'a', inner: 'c' },
  { outer: 'd', inner: 'e' } ]
tlehman
  • 4,808
  • 2
  • 27
  • 48
  • 2
    you can improve on this and retain the original two loops if you combine the first map with the last reduce. ```Object.keys(foo).reduce(function(reduced, key) { return reduced.concat(Object.keys(foo[key]).map(function(val) { return {outer: key, inner: val} } )) }, []);``` – lemieuxster Jan 27 '15 at 21:20
  • 1
    It works, and it is a functional approach, hence I accepted it and +1ed it. Anyway, I'm going to stick with my original version, though ;-) – Golo Roden Jan 28 '15 at 12:08
  • It's good to try and see if other approaches offer something really beneficial. Your approach is much clearer what is going on. – tlehman Jan 28 '15 at 20:21