使用lodash深度展开集合中的所有项

9

我想要展开一个类似这样的数组:

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

转换成这个

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

基本上保留所有没有“items”属性的元素,如果它们有一个,则递归遍历所有深层的“items”数组。
我确实可以编写一个递归函数,但我正在寻找一种漂亮的lodash或underscore方法来解决这个问题。

1
从技术上讲,递归是一种函数式方法... - Joseph Young
您的数据无效 - Nenad Vracar
递归很好看。 - user663031
5个回答

12

在lodash或underscore中没有适用于此的简洁函数。我认为,递归函数是您最好的选择:

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);


2
现在有_.flatMapDeep()函数,它可以递归地扁平化集合。 - Nice-Guy
1
@Nice-Guy,_.flatMapDeep() 不仅仅是 _.map().flattenDeep() 的简写,而且会递归迭代任何子节点。 - William Leung

3

您还可以使用原生的flatMap函数:

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);

如果您想删除属性"items",可以这样做:
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);

0

使用lodash/flatMap,可以用2行代码实现可能的解决方案:

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

这是我凭空想象写下的,所以请谨慎对待。


你有一个使用原生JS的版本和一个使用flattenDeep的lodash版本。我不确定flatMap会带来什么,因为其他方法已经在这篇四年前的文章中涵盖了它们。 - hppycoder
收到,@hppycoder官方!我以后不会在旧帖子上发布任何新的发现或想法了。 - Ivan Breslauer

0
今天遇到了这个问题,惊讶于lodash的flatMapDeep不能解决它。这里是我编写的一个简单递归函数,它可以做你期望的事情,并且同时允许你进行映射。
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
}
  • 第一个参数是您的树形结构
  • 第二个参数是可选的回调函数,用于控制您希望如何映射结果
  • 如果您的嵌套键在树中不是items,则可以使用第三个参数

适用于您用例的示例用法:

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

提取仅包含ID字段的示例:

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

-1

lodash/flattenDeep可以递归地展平一个数组。例如:

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]]}]

请注意,对象内部的嵌套数组不会受到影响,这是您所期望的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接