最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - deep flatten all items in collection using lodash - Stack Overflow

programmeradmin4浏览0评论

I'm looking to flatten an array that look like this:

[{
    "id": 0,
    "text": "item 0"
}, {
    "id": 1,
    "items": [{
        "id": 2,
        "text": "item 2"
    }, {
        "id": 3,
        "items": [{
            "id": 4,
            "text": "item 4"
        }]
    }]
}]

into this

[{
    "id": 0,
    "text": "item 0"
}, {
    "id": 2,
    "text": "item 2"
}, {
    "id": 4,
    "text": "item 4"
}]

basically keeping all element that doesn't have an "items" property, and if they have one, recursively traverse all "items" arrays deep.

I could indeed write a recursive function, but I'm looking for a nice looking lodash or underscore way to solve this.

I'm looking to flatten an array that look like this:

[{
    "id": 0,
    "text": "item 0"
}, {
    "id": 1,
    "items": [{
        "id": 2,
        "text": "item 2"
    }, {
        "id": 3,
        "items": [{
            "id": 4,
            "text": "item 4"
        }]
    }]
}]

into this

[{
    "id": 0,
    "text": "item 0"
}, {
    "id": 2,
    "text": "item 2"
}, {
    "id": 4,
    "text": "item 4"
}]

basically keeping all element that doesn't have an "items" property, and if they have one, recursively traverse all "items" arrays deep.

I could indeed write a recursive function, but I'm looking for a nice looking lodash or underscore way to solve this.

Share Improve this question edited Sep 2, 2016 at 17:52 nox asked Sep 2, 2016 at 17:37 noxnox 3511 gold badge4 silver badges13 bronze badges 3
  • 1 Technically recursion is the functional approach... – Joseph Young Commented Sep 2, 2016 at 17:39
  • Your data is invalid – Nenad Vracar Commented Sep 2, 2016 at 17:40
  • Recursion is nice-looking. – user663031 Commented Sep 2, 2016 at 18:47
Add a comment  | 

5 Answers 5

Reset to default 12

There is no neat function for this in lodash or underscore. I think a recursive function is your best bet:

function collect(array, result) {
  array.forEach(function(el) {
    if(el.items) {
      collect(el.items, result);
    } else {
      result.push(el);
    }
  });
}

var array = [{
    "id": 0,
    "text": "item 0"
}, {
    "id": 1,
    "items": [{
        "id": 2,
        "text": "item 2"
    }, {
        "id": 3,
        "items": [{
            "id": 4,
            "text": "item 4"
        }]
    }]
}];

function collect(array, result) {
  array.forEach(function(el) {
    if(el.items) {
      collect(el.items, result);
    } else {
      result.push(el);
    }
  });
}
var result = [];
collect(array, result);
console.log(result);

You can also use the native flatMap function:

const tree = [{
    "id": 0,
    "text": "item 0"
}, {
    "id": 1,
    "items": [{
        "id": 2,
        "text": "item 2"
    }, {
        "id": 3,
        "items": [{
            "id": 4,
            "text": "item 4"
        }]
    }]
}]

function flattenTree(tree){
   return tree.flatMap( item => item.items ? [item, ...flattenTree(item.items)] : item);
}


flattenTree(tree);

If you want remove the prop "items" you can do this:

const tree = [{
    "id": 0,
    "text": "item 0"
}, {
    "id": 1,
    "items": [{
        "id": 2,
        "text": "item 2"
    }, {
        "id": 3,
        "items": [{
            "id": 4,
            "text": "item 4"
        }]
    }]
}]


function flattenTreeNoItems(tree){
   return tree.flatMap( item => {
    if(item.items){
      const items = item.items;
      delete item.items;
      return [item, ...flattenTreeNoItems(items)];
        }
    return item;
   });
}

flattenTreeNoItems(tree);

Came across this today and surprised that lodash flatMapDeep doesnt work for this. Here's a simple recursive function I wrote that does what you would expect and also allows you to map at the same time.

function flattenTree(items, callback, nestingKey = 'items') {
    let output = []

    items.forEach(item => {
        output.push(callback ? callback(item) : item)
        output = output.concat(flattenTree(item[nestingKey] || [], callback, nestingKey))
    })

    return output
}
  • 1st parameter is your tree structure
  • 2nd parameter is an optional callback function to control how you want the results mapped
  • 3rd parameter can be used if your nesting key in the tree is something other than items

Example usage for your use-case:

let output = flattenTree(items, item => ({
    id: item.id,
    text: item.text
}))

Example to pull out just the ID field:

let ids = flattenTree(items, item => item.id)

Possible solution in 2 lines of code, using lodash/flatMap:

const iteratee = item => (item.items ? _.flatMap(item.items, iteratee) : item);
const flattenedItems = _.flatMap(sourceItems, iteratee);

Written off the top of my head, so take it with a grain of salt.

lodash/flattenDeep will recursively flatten an array. E.g.:

import {flattenDeep} from 'lodash'
const nestedArray = [1, ['2', [3, [{x: 4}]]]]
const mixedNestedArray = [1, ['2', [3, [{x: [[4]]}]]]]

console.log(flattenDeep(nestedArray)) // [1, '2', 3, {x: 4}]
console.log(flattenDeep(mixedNestedArray)) // [1, '2', 3, {x: [[4]]}]

Note that a nested array inside an object won't be affected, which is what you'd expect.

发布评论

评论列表(0)

  1. 暂无评论