简体   繁体   English

嵌套对象递归问题

[英]Nested object recursion trouble

I am trying to write a recursive function that will check each nested object for an even number and return a final sum of those numbers.我正在尝试编写一个递归函数,它将检查每个嵌套对象的偶数并返回这些数字的最终总和。 I am struggling to debug the function I have so far.我正在努力调试到目前为止的功能。

 function nestedEvenSum(obj, sum = 0) { for(const k in obj) { if (obj[k].constructor === Object) { return sum += nestedEvenSum(obj[k], sum); } if (typeof obj[k] === "number" && obj[k] % 2 === 0) { sum += obj[k]; } } return sum; } const obj = { a: 2, c: {c: {c: 2}, cc: 'b', ccc: 5}, e: {e: {e: 2}, ee: 'car'} } console.log(nestedEvenSum(obj));

The function returns 8 .该函数返回8 It should return 6 .它应该返回6

Also, I noticed that it forgets about object e completely, the last object called recursively is {c: 2} .另外,我注意到它完全忘记了对象e ,最后一个递归调用的对象是{c: 2}

This exhibits a classic recursion antipattern: passing the result ( sum ) down the call stack as a parameter while also trying to pass it up as a result, leading to a confused state of affairs and double-counting.这展示了一个经典的递归反模式:将结果 ( sum ) 作为参数向下传递到调用堆栈,同时还试图将其作为结果向上传递,从而导致事态混乱和重复计算。

Here's a fundamental rule of thumb for recursion: data dependencies (the things used to compute a result) are the parameters, results are return values.这是递归的基本经验法则:数据依赖项(用于计算结果的东西)是参数,结果是返回值。

Make sum local to the frame, then accumulate on it during the frame, either because each element is a number (leaf node in the tree search) or it's a child that should be explored recursively.sum设置为帧本地,然后在帧期间对其进行累积,因为每个元素都是一个数字(树搜索中的叶节点),或者它是一个应该递归探索的子元素。 Don't return immediately in the loop or you will miss some of the children.不要在循环中立即return ,否则您会错过一些孩子。

 function nestedEvenSum(obj) { let sum = 0; for (const k in obj) { if (obj[k].constructor === Object) { sum += nestedEvenSum(obj[k]); } else if (typeof obj[k] === "number" && obj[k] % 2 === 0) { sum += obj[k]; } } return sum; } const obj = { a: 2, c: { c: { c: 2 }, cc: 'b', ccc: 5 }, e: { e: { e: 2 }, ee: 'car' } }; console.log(nestedEvenSum(obj));

Note that this algorithm ignores arrays.请注意,此算法忽略数组。

Also note that the function's design is highly rigid due to the % 2 === 0 predicate.另请注意,由于% 2 === 0谓词,该函数的设计非常严格。 You might consider using a function that traverses any nested structure and returns an array or generator of results that can then be filtered, or a function that allows an arbitrary callback predicate to perform the filtering.您可能会考虑使用遍历任何嵌套结构并返回可以过滤的结果的数组或生成器的函数,或者允许任意回调谓词执行过滤的函数。

One exception to the one-way data flow rule is that sometimes you'll want to accumulate results onto a parameter array as an optimization rather than returning and merging multiple arrays as you move back up the call stack, but that doesn't apply here.单向数据流规则的一个例外是,有时您希望将结果累积到参数数组中作为优化,而不是在返回调用堆栈时返回和合并多个数组,但这不适用于此处.

You need to remove the first return statement.您需要删除第一个 return 语句。

To get a shorter code, you could use conditional statements.要获得更短的代码,您可以使用条件语句。

This approach does keep the sum of the actual object and does not hand over the sum to the nested level.这种方法确实保留了实际对象的总和,并且不会将总和交给嵌套级别。

The handing over is only needed for tail call optimization (TCO) because of replacing the old function with the recursive funtion of the stack.尾调用优化(TCO)需要移交,因为用堆栈的递归函数替换旧函数。 Actuall TCO is in Javascript not widely supported ...实际上 TCO 是在 Javascript 中没有得到广泛支持...

 function nestedEvenSum(obj) { let sum = 0; for (const k in obj) { sum += obj[k] && typeof obj[k] === 'object' ? nestedEvenSum(obj[k]) : obj[k] % 2 === 0 ? obj[k] : 0; } return sum; } const obj = { a: 2, c: { c: { c: 2 }, cc: 'b', ccc: 5 }, e: { e: { e: 2 }, ee: 'car' } }; console.log(nestedEvenSum(obj));

I think I figured it out.我想我想通了。

First, you're returning if you find an object, which means you'll stop early, so I removed the early 'return'.首先,如果你找到一个对象,你就会返回,这意味着你会提前停下来,所以我删除了早期的“返回”。

Second, you're double-counting if you find an object, because you're passing in the sum you already have and then adding it to the sum you already have.其次,如果你找到一个对象,你就是在重复计算,因为你传递了你已经拥有的总和,然后将它添加到你已经拥有的总和中。

Check this out, just a couple small changes:看看这个,只有几个小改动:

 function nestedEvenSum(obj, sum = 0) { for(const k in obj) { if (obj[k].constructor === Object) { sum = nestedEvenSum(obj[k], sum); } if (typeof obj[k] === "number" && obj[k] % 2 === 0) { sum += obj[k]; } } return sum; } const obj = { a: 2, c: {c: {c: 2}, cc: 'b', ccc: 5}, e: {e: {e: 2}, ee: 'car'} } console.log(nestedEvenSum(obj));

Why not build this atop a simple deep filtering function?为什么不在一个简单的深度过滤函数之上构建它呢?

 const filterDeep = (p) => (o) => p (o) ? [o] : Object (o) === o ? Object .values (o) .flatMap ((v) => filterDeep (p) (v)) : [] const sum = (ns) => ns .reduce ((a, b) => a + b, 0) const nestedEvenSum = (o) => sum (filterDeep (n => typeof n == 'number' && n % 2 == 0) (o)) const obj = {a: 2, c: {c: {c: 2}, cc: 'b', ccc: 5}, e: {e: {e: 2}, ee: 'car', j: [8, 6, 7, 5, 3, 0, 9]}} console .log (nestedEvenSum (obj))

Here filterDeep accepts a predicate function and returns a function which takes an Object and collects all those (nested) elements which match that predicate.这里filterDeep接受一个谓词函数并返回一个函数,该函数接受一个 Object 并收集所有与该谓词匹配的(嵌套)元素。 With a minor sum helper function, we can now write nestedEvenSum in a very simple manner and have useful helper functions available for other uses.有了一个次要的sum辅助函数,我们现在可以用一种非常简单的方式编写nestedEvenSum并且有有用的辅助函数可用于其他用途。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM