Lodash - 在对象数组中深度查找

15

我有一个像这样的对象数组

[
    {
        'a': 10,
        elements: [
            {
                'prop': 'foo',
                'val': 10
            },
            {
                'prop': 'bar',
                'val': 25
            },
            {
                'prop': 'test',
                'val': 51
            }
        ]
    },
    {
        'b': 50,
        elements: [
            {
                'prop': 'foo',
                'val': 30
            },
            {
                'prop': 'bar',
                'val': 15
            },
            {
                'prop': 'test',
                'val': 60
            }
        ]
    },
]
我需要的是在propfoo时对Val属性求和。因此,我需要搜索元素并获取所有propfoo的对象。使用这些对象,我应该对val属性进行求和。
我尝试了许多_.find_.pick等组合,但是我没有得到正确的结果。有人可以帮我吗?
6个回答

28

这里提供了一种解决方案,它将元素 扁平化 ,然后在 过滤 所需元素后 求和 val 属性的值:

var result = _.chain(data)
    .map('elements')               // pluck all elements from data
    .flatten()                     // flatten the elements into a single array
    .filter({prop: 'foo'})         // exatract elements with a prop of 'foo'
    .sumBy('val')                  // sum all the val properties
    .value()

链式调用是在返回值之前对一些数据应用一系列操作的一种方式。上面的示例使用显式链式调用,但可以(可能应该)使用隐式链式调用进行编写:

链式调用是一种在返回值前对一些数据执行一系列操作的方法。上述示例使用显式链式调用,但可以(也许应该)使用隐式链式调用来编写:

var result = _(data)
    .map('elements')
    .flatten()
    .filter({prop: 'foo'})
    .sumBy('val');

3
似乎是完美的解决方案,但为了更好地理解这个解决方案的未来使用,请在每个方法之后详细解释结果。 - MGA

5

以下是这个问题的一行解决方案:

const _ = require('lodash')

let deepFind = (JSONArray, keyPath, keyValue) => _.find(JSONArray, _.matchesProperty(keyPath, keyValue))

let JSONArray = [{a:1, b:2, c:{d: "cd"}}, {a:3, b:4, c:{d: "ef"}}, {a:3, b:4, c:[{d: "ef"}]} ]

console.log(deepFind(JSONArray, "c.d", "cd"))
// {a:1, b:2, c:{d: "cd"}}

console.log(deepFind(JSONArray, "b", 4)) 
//{a:3, b:4, c:{d: "ef"}}

console.log(deepFind(JSONArray, ['c', 'd'], "cd"))
//{a:1, b:2, c:{d: "cd"}}

console.log(deepFind(JSONArray, 'c[0].d' /* OR ['c', '0', 'd']*/, "ef"))
//{a:1, b:2, c:{d: "cd"}}


5
我已经创建了一个库,你可以使用:https://github.com/dominik791/obj-traverse findAll() 方法应该可以解决你的问题。第一个参数是根对象,不是数组,所以你需要先创建它。
var rootObj = {
  name: 'rootObject',
  elements: [
    {
      'a': 10,
       elements: [ ... ]
    },
    {
       'b': 50,
       elements: [ ... ]
    }
  ]
};

然后使用findAll()方法:

var matchingObjects = findAll( rootObj, 'elements', { 'prop': 'foo' } );

matchingObjects变量存储所有 prop 等于 foo 的对象。最后计算它们的总和:

var sum = 0;
matchingObjects.forEach(function(obj) {
  sum += obj.val;
});

1
披露:我是该库的作者。
你可以使用 deepdash 中的 _.eachDeep 方法:
let sum = 0;
_.eachDeep(obj, (value, key, parent) => {
  sum += (key == 'prop' && value == 'foo' && parent.val) || 0;
});

这里是您的案例的示例


3
这个库的潜力很大,但文档编写得很差。 - corysimmons
谢谢。您能否提供更详细的反馈意见,GitHub 上的问题是可以接受的。 - Yuri Gor
1
只需为每个函数提供2个示例(尽可能简单和中级),但最好提供最简单的示例。 - corysimmons

0
你可以循环遍历数据并求和。试试这个方法:

var arr=[
     {
         'a': 10,
         elements: [
             {
                 'prop': 'foo',
                 'val': 10
             },
             {
                 'prop': 'bar',
                 'val': 25
             },
             {
                 'prop': 'test',
                 'val': 51
             }
         ]
     },
     {
         'b': 50,
         elements: [
             {
                 'prop': 'foo',
                 'val': 30
             },
             {
                 'prop': 'bar',
                 'val': 15
             },
             {
                 'prop': 'test',
                 'val': 60
             }
         ]
     }
 ];
 var sum=0;
 for(x in arr){
 for(i in arr[x]['elements']){
   if(arr[x]['elements'][i]['prop'] == 'foo'){
    sum=sum+arr[x]['elements'][i]['val'];
   }
 }

 }

 console.log(sum);

使用过滤器:

var arr=[
     {
         'a': 10,
         elements: [
             {
                 'prop': 'foo',
                 'val': 10
             },
             {
                 'prop': 'bar',
                 'val': 25
             },
             {
                 'prop': 'test',
                 'val': 51
             }
         ]
     },
     {
         'b': 50,
         elements: [
             {
                 'prop': 'foo',
                 'val': 30
             },
             {
                 'prop': 'bar',
                 'val': 15
             },
             {
                 'prop': 'test',
                 'val': 60
             }
         ]
     }
 ];
 var sum=0;

arr.filter(function (person) { 
  person['elements'].filter(function(data){
  if (data.prop == "foo"){
    sum=sum+data.val;
  }
  });
  });
           
           console.log(sum);


0

如果有人需要,这是对我有效的解决方案。但我的情况有点不同,我需要找到与ID匹配的数组。

这里有一个流程函数,虽然不是必需的,但它确实使代码更易于阅读。

const flow = functions => data =>
    functions.reduce((value, func) => func(value), data);

   const arr = [
        {
            'a': 10,
            elements: [
                {
                    'prop': 'foo',
                    'val': 10
                },
                {
                    'prop': 'bar',
                    'val': 25
                },
                {
                    'prop': 'test',
                    'val': 51
                }
            ]
        },
        {
            'b': 50,
            elements: [
                {
                    'prop': 'foo',
                    'val': 30
                },
                {
                    'prop': 'bar',
                    'val': 15
                },
                {
                    'prop': 'test',
                    'val': 60
                }
            ]
        },
    ]

    const result= _.flow([
        data => _.map(data, 'elements'), // get all elements from the array 
        data => _.flatten(data), // create a singler array 
        data => _.find(data, { val: 65 }) // find the needed value in array 
        // you can keep on adding logic here depending on your need
    ])(arr); // the inital array 
    

如果你不想使用流程函数,我认为也可以这样做。我没有测试过代码,但应该可以工作。

const results = _.find(_.flatten(_.map(arr, 'elements')), { val: 65 });

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