如何将以下递归函数转换为纯函数?

时间:2016-04-16 07:19:08

标签: javascript recursion functional-programming

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

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

3 个答案:

答案 0 :(得分:2)

您未在findDeep内向您返回递归map方法。返回表示递归工作,因为条件分支没有从map中返回任何内容。因此,您获得的结果为undefinedJSBin

答案 1 :(得分:1)

这是一项使用Array#somethisArgs的提案。



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>');
&#13;
&#13;
&#13;

答案 2 :(得分:1)

首先,一个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不会被修改,也不需要克隆。我还没弄明白如何正确地做到这一点。