以下JavaScript减少函数的模式是什么?

时间:2016-04-16 14:25:13

标签: javascript mapreduce

我无法理解以下缩减功能:

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, curr) => {
    console.log('prev: ', prev)
    console.log('curr: ', curr)
    return prev || curr
  })
}

应用于此对象:

const mockApps = {
  name: 'orange',
  children: [{
    name: 'white'
  }, {
    name: 'green',
    children: [{
      name: 'yellow',
      children: [{
        name: 'red'
      }, {
        name: 'white'
      }]
    }, {
      name: 'green',
      children: [{
        name: 'purple'
      }]
    }]
  }, {
    name: 'gray'
  }]
}

const activeApp = {
  name: 'purple',
  color: 'purple',
  path: 'writer'
}

findDeep(mockApps.children, activeApp)

我认为这种模式就像MDN中的例子:

[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, currentIndex, array) {
  return previousValue + currentValue;
});

enter image description here

但令我惊讶的是,我理论化的与输出不同:

enter image description here

我认为previousValue将是上一次迭代的returnValue,但正如您在控制台中看到的那样,即使prev,第三个currentValue也未定义之前的迭代不是。

这个reduce功能的正确模式是什么?

这里是CodePen

2 个答案:

答案 0 :(得分:1)

您似乎假设控制台输出属于reduce的一次运行,但事实并非如此。

函数findDeep以递归方式调用自身,因此您可以从对reduce的不同调用中获得输出。

我建议您按照以下方式修改代码,以便在调用和退出findDeep时在控制台中查看:

function findDeep(arr, obj) {
  console.log('Entering findDeep');
  var res = arr.map(item => {
    if (item.name === obj.name) {
      return arr
    } else if (item.children) {
      return findDeep(item.children, obj)
    } else {
      return undefined
    }
  }).reduce((prev, curr) => {
    console.log('prev: ' + JSON.stringify(prev));
    console.log('curr: ' + JSON.stringify(curr));
    console.log('return: ' + JSON.stringify(prev || curr));
    return prev || curr;
  });
  console.log('Exiting from findDeep');
  return res;
}

这应该为这个问题带来光明。这是一个将日志写入浏览器的片段:

console = { log: function(msg) {
  document.write(msg + '<br>');
}}

function findDeep(arr, obj) {
  console.log('Entering findDeep');
  var res = arr.map(item => {
    if (item.name === obj.name) {
      return arr
    } else if (item.children) {
      return findDeep(item.children, obj)
    } else {
      return undefined
    }
  }).reduce((prev, curr) => {
    console.log('prev: ' + JSON.stringify(prev));
    console.log('curr: ' + JSON.stringify(curr));
    console.log('return: ' + JSON.stringify(prev || curr));
    return prev || curr;
  });
  console.log('Exiting from findDeep');
  return res;
}

const mockApps = {
  name: 'orange',
  children: [{
    name: 'white'
  }, {
    name: 'green',
    children: [{
      name: 'yellow',
      children: [{
        name: 'red'
      }, {
        name: 'white'
      }]
    }, {
      name: 'green',
      children: [{
        name: 'purple'
      }]
    }]
  }, {
    name: 'gray'
  }]
}

const activeApp = {
  name: 'purple',
  color: 'purple',
  path: 'writer'
}

findDeep(mockApps.children, activeApp)

正如您所看到的,以前似乎prev的值与前一次迭代的返回值不对应,现在很明显这两次迭代属于{{1}的不同调用},所以它的行为就像你期望的那样。

答案 1 :(得分:1)

如果您遵循代码,传递给 map (级别0)的第一个值是:

{name: 'white'}

它没有紫色或任何子项的名称,因此结果数组现在是:

[undefined]

下一项是:

{name: 'green',
 children: [{
   name: 'yellow',
   children: [{
     name: 'red'
     }, {
     name: 'white'
     }]
   }, {
     name: 'green',
     children: [{
       name: 'purple'
     }]
   }]
}

它有一个 children 属性,因此它的值被传递给 findDeep (级别1)的递归调用,即:

 [{
   name: 'yellow',
   children: [{
     name: 'red'
     }, {
     name: 'white'
     }]
   }, {
     name: 'green',
     children: [{
       name: 'purple'
     }]
 }]

传递给 map 的第一项,再次 findDeep 以递归方式调用(级别2):

   [{name: 'red'},
    {name: 'white'}]
   }, {
     name: 'green',
     children: [{
       name: 'purple'
     }]

第一个项目没有紫色或子项的名称,所以此级别的地图数组现在是:

[undefined]

下一个项目相同,所以现在是:

[undefined, undefined]

下一个名称为“紫色”,因此它被添加到数组中:

[undefined, undefined,{name:'purple'}]

这是通过 reduce 运行的,它是在没有累加器的情况下调用的,所以前两个值作为 prev cur 传递。由于 prev 是假的,因此 curr 的值将作为累加器返回,因此在下次调用时,值为 undefined {name:'purple'} ,所以它返回到1级地图,它的数组现在是:

[{name:'purple'}]

此级别中没有更多成员,因此传递给reduce。因为它是数组中唯一的成员,并且没有传入累加器,所以只返回它,因此1级结果是:

[{name:'purple'}]

级别0的最后一个成员也返回undefined,因此最终的0级数组是:

[{name:'purple'}, undefined]

传递给reduce的是两个值,分别是 prev curr 。由于 prev 该对象不是假的,所以返回它并且最终结果是:

[{name:'purple'}]

请注意,如果您使用 JSON.stringify 查看对象和数组, undefined 将更改为“null”。