4

I have an array of object with hierarchical structure, something like this:

[
    {name: 'ParentOne', children: [
        {name: 'ParentOneChildOne'},
        {name: 'ParentOneChildTwo', children: [
            {name: 'ParentOneChildTwoGrandChildOne'},
        ]},
    ]}, 
    {name: 'ParentTwo', children: [
        {name: 'ParentTwoChildOne', children: [
           {name: 'ParentTwoChildOneGrandChildOne'},
           {name: 'ParentTwoChildOneGrandChildTwo'}
        ]},
        {name: 'ParentTwoChildTwo'}
    ]}
];

I want to flatten it:

[
    {name: 'ParentOne'},
    {name: 'ParentOneChildOne'},
    {name: 'ParentOneChildTwo'},
    {name: 'ParentOneChildTwoGrandChildOne'},
    {name: 'ParentTwo'},
    {name: 'ParentTwoChildOne'},
    {name: 'ParentTwoChildOneGrandChildOne'},
    {name: 'ParentTwoChildOneGrandChildTwo'},
    {name: 'ParentTwoChildTwo'}
]

I have tried _.flatten() and _.flatMap(), but it does not produce what I need. What is the best way to achieve it preferably using lodash.js or underscore.js.

5 Answers 5

15

No need for underscore/lodash.

const arr = [
    {name: 'ParentOne', children: [
        {name: 'ParentOneChildOne'},
        {name: 'ParentOneChildTwo', children: [
            {name: 'ParentOneChildTwoGrandChildOne'},
        ]},
    ]}, 
    {name: 'ParentTwo', children: [
        {name: 'ParentTwoChildOne', children: [
           {name: 'ParentTwoChildOneGrandChildOne'},
           {name: 'ParentTwoChildOneGrandChildTwo'}
        ]},
        {name: 'ParentTwoChildTwo'}
    ]}
];

function flatten(arr) {
    return arr? arr.reduce((result, item) => [
        ...result,
        { name: item.name },
        ...flatten(item.children)
    ], []) : [];
}

console.log(flatten(arr));

Sign up to request clarification or add additional context in comments.

Comments

2

Recursive functions is the way to go for any depth of iteration.

With some ES2015 and LoDash/Underscore

var arr = [{
  name: 'ParentOne',
  children: [{
    name: 'ParentOneChildOne'
  }, {
    name: 'ParentOneChildTwo',
    children: [{
      name: 'ParentOneChildTwoGrandChildOne'
    }, ]
  }, ]
}, {
  name: 'ParentTwo',
  children: [{
    name: 'ParentTwoChildOne',
    children: [{
      name: 'ParentTwoChildOneGrandChildOne'
    }, {
      name: 'ParentTwoChildOneGrandChildTwo'
    }]
  }, {
    name: 'ParentTwoChildTwo'
  }]
}];

var res = _.reduce(arr, (a, b) => {
  (rec = item => {
    _.each(item, (v, k) => (_.isObject(v) ? rec(v) : a.push(_.zipObject([k], [v]))))
  })(b);
  return a;
}, []);

console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Comments

1

I would use .reduce and recursion to do this. Here's my implementation using Array.reduce, but you could do much the same with underscore's reduce function.

const arr = [
    {name: 'ParentOne', children: [
        {name: 'ParentOneChildOne'},
        {name: 'ParentOneChildTwo', children: [
            {name: 'ParentOneChildTwoGrandChildOne'},
        ]},
    ]}, 
    {name: 'ParentTwo', children: [
        {name: 'ParentTwoChildOne', children: [
           {name: 'ParentTwoChildOneGrandChildOne'},
           {name: 'ParentTwoChildOneGrandChildTwo'}
        ]},
        {name: 'ParentTwoChildTwo'}
    ]}
];

function flatten(arr) {
   return arr.reduce((result, current) => {
      if (current.children) {
         const children = flatten(current.children);
         delete current.children;
         result.push(current);
         result.push(...children);
      } else {
         result.push(current);
      }
      return result;
   }, [])
}

console.log(flatten(arr));

Comments

0

You could try adapting the flatten function given in this answer, and slightly twist the logic to your object's structure.

//Your object
var data = [{
    name: 'ParentOne',
    children: [{
        name: 'ParentOneChildOne'
      },
      {
        name: 'ParentOneChildTwo',
        children: [{
          name: 'ParentOneChildTwoGrandChildOne'
        }, ]
      },
    ]
  },
  {
    name: 'ParentTwo',
    children: [{
        name: 'ParentTwoChildOne',
        children: [{
            name: 'ParentTwoChildOneGrandChildOne'
          },
          {
            name: 'ParentTwoChildOneGrandChildTwo'
          }
        ]
      },
      {
        name: 'ParentTwoChildTwo'
      }
    ]
  }
];

//georg's flatten function
flatten = function(x, result, prefix) {
  if (_.isObject(x)) {
    _.each(x, function(v, k) {
      flatten(v, result, prefix ? prefix + '_' + k : k)
    })
  } else {
    result[prefix] = x
  }
  return result
}

//using the function on your data
result = flatten(data, {});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

Does this help at all?

Comments

0

You could use some destruction and use a recursive function for collecting all wanted items.

var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
    flat = (r, { name, children = [] }) => [...r, { name }, ...children.reduce(flat, []) ],
    result = array.reduce(flat, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

On EDGE, you need a different use of default values.

var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
    flat = (r, { name, children }) => [...r, { name }, ...(children || []).reduce(flat, []) ],
    result = array.reduce(flat, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.