【问题标题】:How to turn the following recursive function into a pure function?如何将下面的递归函数变成纯函数?
【发布时间】:2016-08-08 06:19:46
【问题描述】:

以下函数将对象附加到嵌套数组中(通过递归搜索):

function appendDeep (arr, obj, newObj) {
  if (arr.indexOf(obj) !== -1) {
    arr.splice(arr.indexOf(obj) + 1, 0, newObj)
  } else {
    arr.map(item => {
      if (item.children) spliceDeep(item.children, obj)
    })
  }
}

例子:

const colors = {
  children: [
    {
      name: 'white',
    },
    {
      name: 'yellow',
      children: [
        {
          name: 'black'
        }
      ]
    }
  ]
}

const color = {
  name: 'black'
}

const newColor = {
  name: 'brown'
}

appendDeep(colors.children, color, newColor)

结果:

children: [
     [
       {
         name: 'white',
       },
       {
         name: 'yellow',
         children: [
           {
             name: 'black'
           },
           {
             name: 'brown'
           }
         ]
       }
     ]
   ]

如您所见,appendDeep 返回一个副作用;它修改了arr。所以我决定改为返回数组(这样函数就会变成纯函数):

function findDeep (arr, obj) {
  if (arr.indexOf(obj) !== -1) {
    console.log(arr)
    return arr
  } else {
    arr.map(item => {
      if (item.children) findDeep(item.children, obj)
    })
  }
}

并像这样使用新功能:

const newArr = findDeep(colors.children, color)
newArr.splice(newArr.indexOf(color) + 1, 0, newColor)

但我收到此错误:

bundle.js:19893 Uncaught TypeError: Cannot read property 'splice' of undefined

我做错了什么?

(注意:这里是CodePen。)

(注 2:console.log(arr) 确实返回嵌套的子级。但由于某种原因,它们在函数之外变成了undefined。)

【问题讨论】:

  • 请将colornewColor 更改为有效对象。请也添加想要的结果。
  • @NinaScholz 好的,我进行了更改。
  • colors.children 是一个包含一个项目的数组,它是另一个数组。这可能不是您想要的,当然也不是您的代码所期望的。
  • @Arjan 你是对的。我更新了问题和示例。

标签: javascript recursion functional-programming


【解决方案1】:

您不会在map 中返回递归findDeep 方法。返回它以使递归工作,因为您的条件分支没有从地图中返回任何内容。结果,您得到的结果为undefinedJSBin

【讨论】:

  • 我试过这个:arr.map(item => { if (item.children) return findDeep(item.children, obj) }),但我仍然收到undefined
  • 我添加了一个带有工作代码示例的 JSBin。现在检查。它应该工作
  • 使用您的代码,我得到如下信息:[undefined, Array[2], undefined]。而不是匹配的数组。
【解决方案2】:

这是一个使用thisArgsArray#some 的提案。

function appendDeep(object, search, insert) {
    function iter(a) {
        if (a.name === search.name) {
            this.children.push(insert);
            return true;
        }
        return Array.isArray(a.children) && a.children.some(iter, a);
    }

    object.children.some(iter, object);
}

var colors = { children: [{ name: 'white', }, { name: 'yellow', children: [{ name: 'black' }] }] },
    color = { name: 'black' },
    newColor = { name: 'brown' };

appendDeep(colors, color, newColor);

document.write('<pre>' + JSON.stringify(colors, 0, 4) + '</pre>');

【讨论】:

    【解决方案3】:

    首先,一个 find 方法将返回所请求项所在的数组(作为直接子项)。

    function findDeep(arr, obj) {
        return arr.map((item) => {
            if (item.name === obj.name) {
                return arr;
            } else if (item.children) {
                return findDeep(item.children, obj);
            } else {
                return undefined;
            }
        }).reduce((prev, cur) => {
            return prev ? prev : cur;
        });
    }
    

    您可以使用它来将项目附加到列表中,但这仍会修改原始数组:

    function appendDeep(arr, color, newColor) {
        let found = findDeep(arr, color);
    
        if (found) {
            found.splice(found.indexOf(color) + 1, 0, newColor);
        }
    
        return arr;
    }
    

    如果您不想修改原始数组,事情会变得更加复杂。那是因为pushsplice等标准数组函数会修改原始数组。没有快速修复,至少我不知道,因为最好不要克隆比实际需要的更多的项目。

    你不需要克隆black,但你需要克隆包含它的数组(它可以简单地重用现有的黑色对象。)这意味着黄色的对象也需要被克隆(到使用克隆的数组),需要克隆黄色所在的数组。但是同一个数组中的white没有被修改,也不需要克隆。我还没有弄清楚如何正确地做到这一点。

    【讨论】:

    • 嘿,非常感谢。我花了一整天的时间在这上面。 findDeep 一直在记录 undefined。使一切正常工作的部分是.reduce((prev, cur) =&gt; { return prev ? prev : cur })。为什么它与那件作品一起工作而不是没有它?顺便问一下prev ? prev : curprev || cur不一样吗?
    • map 通过将函数应用于数组的每个元素,将一个数组转换为另一个数组。 reduce 通过将函数应用于每个元素 (cur) 并为函数提供先前或中间结果,将数组转换为单个项目。您还可以提供初始值。在这种情况下,我不需要初始值。是的,prev || cur 产生与prev ? prev : cur 相同的结果。我不知道两者是否还有其他好处。
    • prev ? prev : cur 在 ESLint 中抛出 Uneccesary use of conditoinal assignment,哈哈。感谢您的帮助和解释一切。
    猜你喜欢
    • 2015-06-27
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    相关资源
    最近更新 更多