【问题标题】:Parent value as sum of all children values within nested javascript object父值作为嵌套 javascript 对象中所有子值的总和
【发布时间】:2017-01-19 15:36:46
【问题描述】:

我有一个深度嵌套的 javascript 对象,其中包含无限数量的子对象。每个孩子都有一个价值和一个总价值。 totalValue 应该是其所有子项和子项的所有值的总和。我怎样才能做到这一点?

目前我只能使用递归函数循环整个对象:

// Recursive function
_.each(names, function(parent) { 
    if(parent.children.length > 0) { 
        recursiveFunction(parent.children);
    }
});

function recursiveFunction(children){ 
    _.each(children, function(child) { 
        if(child.children.length > 0) { 
            recursiveFunction(child.children)
        }
    });
}; 

// Deeply nested javascript object
var names = {
    name: 'name-1',
    value: 10,
    valueTotal: 0, // should be 60 (name-1.1 + name-1.2 + name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
    children: [{
            name: 'name-1.1',
            value: 10,
            valueTotal: 0,
            children: []
        }, {
            name: 'name-1.2',
            value: 10,
            valueTotal: 0, // should be 40 (name-1.2.1 + name-1.2.2 + name-1.2.2.1 + name-1.2.2.2)
            children: [{
                name: 'name-1.2.1',
                value: 10,
                valueTotal: 0,
                children: []
            }, {
                name: 'name-1.2.2',
                value: 10,
                valueTotal: 0, // should be 20 (name-1.2.2.1 + name-1.2.2.2)
                children: [{
                    name: 'name-1.2.2.1',
                    value: 10,
                    valueTotal: 0,
                    children: []
                }, {
                    name: 'name-1.2.2.2',
                    value: 10,
                    valueTotal: 0,
                    children: []
                }]
            }]
        }]
    }
}

【问题讨论】:

  • 您的总和看起来不对,name-2 是 name-1 的孩子,应该包括在内。
  • 你的权利!我会改的。

标签: javascript recursion lodash


【解决方案1】:

所以实际上你想做这样的事情: 每个 elem 都会向他的孩子询问其值,这些孩子也会这样做并返回他们的 totalValues 加上他们自己的值。

function sumUp(object){
  object.totalValue = 0;
  for(child of object.children){
    object.totalValue += sumUp(child);
   }
   return object.totalValue + object.value;
}

这样开始:

const totalofall = sumUp(names);
console.log(names); //your expected result.

工作示例: http://jsbin.com/laxiveyoki/edit?console

【讨论】:

  • 我有点困惑,我的目标不是获取对象内所有值的总值。我试图仅用其子/子值的总和填充每个节点的每个 totalValue。结果应该是相同的列表,但填充了所有 totalValues(仅当该节点包含子节点时)。
  • @Sam 它通过 object.totalValue+=sumUp(children);
  • 查看示例树,它已记录。您会发现 totalValue 包含您要搜索的内容
  • 我确实看错了,我会尽快测试它是否有效!
  • 请编辑您的回复,我将删除一票(我的错误,看起来值实际上是正确的)
【解决方案2】:

您可以使用带有迭代和递归方法的纯 Javascript,并对本地总数进行一些健全性检查。

function calculateValues(o) {
    o.valueTotal = (o.children || []).reduce(function (r, a) {
        calculateValues(a);				
        return r + (a.value || 0) + (a.valueTotal || 0);
    }, 0);
}

var names = { name: 'name-1', value: 10, valueTotal: 0, children: [{ name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 0, children: [{ name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 0, children: [{ name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] }] }] }] };

calculateValues(names);
console.log(names);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案3】:

    不太喜欢重新发明轮子,建议使用库来提高可读性和可维护性。我们现在使用object-scan 进行大部分数据处理。一旦您了解如何使用它,它就非常通用。无论如何,这是您问题的可能解决方案

    // const objectScan = require('object-scan');
    
    const injectSums = (data) => {
      objectScan(['**.children'], {
        filterFn: ({ parent, value: children }) => {
          parent.valueTotal = children
            .map(({ valueTotal, value }) => valueTotal + value)
            .reduce((a, b) => a + b, 0);
        }
      })(data);
    };
    
    const names = { name: 'name-1', value: 10, valueTotal: 0, children: [ { name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 0, children: [ { name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 0, children: [ { name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] } ] } ] }] };
    
    injectSums(names);
    
    console.log(names);
    // => { name: 'name-1', value: 10, valueTotal: 60, children: [ { name: 'name-1.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2', value: 10, valueTotal: 40, children: [ { name: 'name-1.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2', value: 10, valueTotal: 20, children: [ { name: 'name-1.2.2.1', value: 10, valueTotal: 0, children: [] }, { name: 'name-1.2.2.2', value: 10, valueTotal: 0, children: [] } ] } ] } ] }
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://bundle.run/object-scan@13.8.0"></script>

    免责声明:我是object-scan的作者

    【讨论】:

    • 如果您想解决旧问题并展示object-scan 如何帮助回答这些问题,请随意,如果这只是您的新锤子,因此一切看起来都像钉子,那也很好。但如果您与object-scan 有重要联系,请附上免责声明。 (例如,在讨论 Ramda 时,我会添加类似“(免责声明:我是 Ramda 的主要作者之一)”之类的内容。)因为感觉好像你想在这里卖东西。
    • 请尝试为 JS 答案写一个snippet。您可以使用 <script src="https://bundle.run/object-scan@13.6.6"></script> 之类的东西来代替 import 语句。
    • @ScottSauyet 感谢您的反馈。我的意图显然不是出售任何东西,而是将这些老问题作为自己的学习练习。但你是绝对正确的,我会仔细检查我的答案,稍后用免责声明更新它们。
    • 如果您连接到图书馆(可能是主要贡献者或资助者),则免责声明很重要。否则,没有必要。我刚刚看到你对一些旧问题的最新答案的相同介绍,我想知道它。
    猜你喜欢
    • 2017-09-30
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 2020-02-21
    • 2021-06-07
    • 2017-03-05
    • 2018-06-21
    • 2017-11-24
    相关资源
    最近更新 更多