zoukankan      html  css  js  c++  java
  • 使用 Array.prototype.reduce()可减少对数组问题的概念性引用

    原文来自[https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/JS-Reduce-Made-Easy]

      这太拗口了。它之所以被称为reduce(),是为了能更方便解决数组问题。所以更容易读取和理解。在JS中,for循环就不是这样的。他们不是精炼的,他们得让你绕一会儿。笑话归笑话,在计算机科学中,有两大问题是难解决的:缓存无效,命名。还有一种危险:写异步代码里面包含里不使用闭包的for循环。这篇文章将从一个声明开始:你可以避免使用一个for循环或while循环来解决任何数组相关的问题。相反,你可以用reduce()来解决他们所有的问题。如果你读之前,务必了解递归函数,和一些很酷的功能工具如map()或filter()。实践出真知。让我们展如何能习惯用reduce()。你该知道,如果你没有做完freecodecamp算法部分题目,你可能要停止这一部分阅读。那里有一些题目可以很好地体现这些问题。若没有做过,这可能是一篇文章陈词滥调的剧透。确保你对这些问题能自己独立动手,而不是采取偷看之前解决方案。此外,如果你已经足够好了解它,欢迎浏览,并提供反馈。


       我可以减少任何数组相关的问题吗?

        让我们采取一个例子。来创建一个很常见的网站的英文标题:标准的连字符间隔的字符串,如新闻标题,博客文章标题,甚至问一个论坛的问题。现在,我们来写一个实用的函数,创建这个标题。你可能会写成这样: 

    function createSlug(str){
      return str.split(" ").reduce(function(prev, next){
        return prev.concat([next.toLowerCase()]);
      }, [])
      .join("-");
    }
    

      来测试一下你的控制台输入像“Leo Finally Wins a Freaking Oscar!”看看它的回应。是的,它不是一个强大的实现。将空格替换为“-”,首尾不需要替换。结果如下:

    leo-finally-wins-a-freaking-oscar
    

      注意,最重要的下面这一条语句:  

    return prev.concat([next.toLowerCase()]);
    

      这是我们想要的功能的核心:我们的函数体开始返回语句!

     


     

     但我一点也不明白Reduce()!

    每一个JavaScript函数有三件事你需要知道,了解函数的运作流程:

    • 输入
    • 输出
    • 执行上下文

      打开官方文档,你可能对于回调函数里的:prev 和 next 感到困惑。Array.prototype.reduce()将回调函数(callback)和初始值(initialValue)作为输入参数,其中,初始值(initialValue)是非常重要的,许多开发人员都忘记提供正确的初始值,而导致代码错误。

      正如你所看的文档,它需要一些额外的,但可选的参数,以及更多。假设 arr 是一个任意的数组。

    arr.reduce(function(){}, initialValue);
    

      现在,让我们仔细看看回调函数,这是reduce()的第一个参数,它也需要两个参数。

      在本文中,我们会称他们为acc,代表累积值;和item,表示正在访问的当前项目。

      目前为止,我们的reduce()应该长成这样:  

    arr.reduce(function(acc, item){
     /*函数内容*/
    }, initialValue);
    

      现在,让我们找出将这些acc和item的值。我们前面提到的,reduce是一个迭代结构的替换。它将减少将采取自定义回调函数,遍历该数组减少被调用。为了更进一步的了解reduce()的迭代过程,我们用conso.log()来看看:

    var arr = [10, 20, 30, 60];
    arr.reduce(function(acc, item){
       console.log(acc, item);
    }, 0);
    

      输出如下:

    0 10
    undefined 20
    undefined 30
    undefined 60
    

      注意输出的数和数组中的元素数相同[ 60,20,30,10 ]。事实上,它打印出数组的元素! 

    因此,我们可以推断出,reduce()需要自定义回调并执行它对数组的每个元素。这样做时,它使当前的项目可用到自定义回调作为项目参数。但对于acc?我们看到,除了第一行,当item= 10时,它是不确定的。在第一行,对应的第一次迭代,其价值是相同的初值。总之,我们的acc累加器,不积累!但是,我们如何使它积累?让我们尝试执行这个:

    var arr = [10, 20, 30, 60];
    arr.reduce(function(acc, item){
       console.log(acc, item);
       return acc;
    }, 0);
    

      这一次的输出变化:

    0 10
    0 20
    0 30
    0 60

    你可以看到,acc的值将保持不变。这是预期之中的,在自定义回调中,我们不改变acc的任何价值。我们返回任何减少,使在一个给定的迭代。但我们没有意识到的东西——acc当前迭代的值,将从自定义回调从以前的迭代的返回值。最终,当迭代结束,最终的价值将减少调用返回的ACC。
    这只留下一个重要的部分,在我们的理解——执行上下文的值,或this!

    var arr = [10, 20, 30, 60];
    arr.reduce(function(acc, item){
       console.log(acc, item, this);
       return acc;
    }, 0);
    

      

    如果你是在严格的模式,它会返回不确定的值,这。否则,在浏览器中,它会指向这个窗口对象。我们可以覆盖和设置它在我们自己的,使用绑定?当然!只使用绑定与回调:

    var arr = [10, 20, 30, 60];
    arr.reduce(function(acc, item){
       console.log(acc, item, this);
       return acc;
    }.bind(arr), 0);
    

      我已经绑定数组arr本身;但你可以将它设置为在你的环境中的任何对象。

     


    了解Reduce

     让我们总结一下我们对这个减少函数的理解,便于参考: 

    • 减少以一个自定义回调函数作为其第一个参数,并将其作为第二个参数的一些初始值。
    • 重要的是,我们不要忘记第二个参数,初始值;我们明确地设置它,而使用它。
    • 要自定义回调的输入参数的累积值acc;和当前项目在数组中,项目。
    • acc的下一次迭代的值将返回值在回调,在当前迭代。
    • 使用reduce()整点是形成正确的acc;最后从reduce()回调。

    你不用试着临时抱佛脚记住他们!相反,把它们应用在实际的代码中。


    减少使用

    让我们在一个数组中开始一个简单的数组操作,在一个数组中查找最大值。为了简单起见,我设它是一个整数数组。
    为了形成一个解决方案,我们需要考虑如何形成acc和遍历数组。我觉得一个有用的方法:出一个制定,无论数组的大小或内容;ACC应该迄今最大值。比如:我的数组是[ 60,50,5,20 ]。经过两次迭代;项目将5和ACC应该是最大的(20,50)= 50。只有这样acc总是最大值。

    var arr = [20, 50, 5, 60];
    arr.reduce(function(acc, item){
      return Math.max(acc, item);
    }, 0);
    

      如果你想写得更精炼:

    var arr = [20, 50, 5, 60];
    arr.reduce(Math.max, 0);
    

      但这不会工作,将返回NaN。原因是:acc 和 item 不仅仅是回调函数的参数。当你调用Math.max()时,max()会试图把它称为非数值的参数,导致结果为NaN。  

      那么,如果我的数组是由小于零的值组成呢?比如,arr = [ - 7、- 56、- 5、- 2 ]。返回的值是0,这是不存在于数组arr。相反,我们应该选择初始值的最低可能值。

    var arr = [-20, -50, -5, -60];
    arr.reduce(function(acc, item){
      return Math.max(acc, item);
    }, -Infinity);
    

      


    更多的reduce()

      Reduce不只是一个函数为您提供工具来解决一些数学问题如矩阵阵,HCF的阵列,数组中的最小值。它是完全能够超越和超越。我们将在处理现在一些复杂的例子。比如:你希望扁平化嵌套数组。例如,我们可以使用这个数组来测试我们的代码。

    var arr = [[1, 2, 3], ['cat', 'dog', ['fish', 'bird'], [[[]]]]];
    

      这看起来足够复杂,开始与嵌套的数组,不同深度的空嵌套数组。输出应该是:

    [1, 2, 3, 'cat', 'dog', 'fish', 'bird']
    

      我们显然需要区分一个数组和一个元素。同时,acc应该在整个迭代形成数组;这意味着初始值将是一个空数组。在整个回调函数代码中,我们只简单地从项目中提取内容,它可以是一个嵌套的数组。我们将 Array.prototype.concat()与acc值。它的更好的使用concat()在 Array.prototype.push();因为push()改变原数组,而concat()创建一个新数组并返回它。因为我们不知道在任何一个给定的即时嵌套的水平;我们必须递归调用我们的自定义回调。的意思,我们已经在其他地方写它的名字里面reduce()称它。

    var arr = [[1, 2, 3], ['cat', 'dog', ['fish', 'bird'], [[[]]]]];
    
    function flattenArray(arr) {
      return arr.reduce(function(acc, item){
        if(Array.isArray(item)){
          return item.reduce(flattenArray, acc);
        }
        return acc.concat(item); // this does the ordering. If you want reverse ordered output, just reverse it!
      }, [])
    
    }
    
    // call it like this
    flattenArray(arr);
    

      当然,这需要在递归函数中的一些背景;但这并不太难拿起,相比这个长的问题!但是注意,我们可以简单地写3-4行清洁功能保持一些简单的指导思想,做一些复杂的,可靠的。这是可读性和可维护性。

      例如,如果你想改变或调整后的逻辑(说你想上的情况下,一些字符串或编码一些字符串);你可以很容易地确定在哪里改变。实际嵌套在中频条件下发生。和我们使用的减少调用有-它保持元素的顺序,因为它们是在数组中。我们要找出两个或两个以上数组的对称差异。它看起来令人畏惧;但你开始思考。最初的价值是什么?当然,我们正在形成一个数组;因此,它将是一个空数组[ ]开始。然后是acc ——因为我们的最终解决方案将包含一个diff ED阵列;它也将是一个数组。这将继续打桩的阵列遇到的对称差异,到目前为止。只要是清晰的,这个函数可以接受任意数量的数组;因此,我们必须把它们转换成一个数组的数组。

    function symDiff(args){
      // convert args to an Array
      var argsArray = Array.prototype.slice.call(arguments);
    
      // now do the reduce magic!
      argsArray.reduce(function(acc, item){
        return acc
          .filter(function(itemInAcc){
            return item.indexOf(itemInAcc) === -1;
          })
          .concat(item.filter(function(itemInItem){
            return acc.indexOf(itemInItem) === -1;
          }));
      }. []);
    }
    

      是的,我知道。它看起来大。所以,让我们看看我们是否能够重构使其小。注意,两个过滤器功能都做同样的工作;除非用改变的参数对。让我们创建一个单独的函数,并用这些参数调用它两次。

    function symDiff(args){
      // convert args to an Array
      var argsArray = Array.prototype.slice.call(arguments);
    
      // now do the reduce magic!
      argsArray.reduce(function(acc, item){
        var funWithFiltering = function(arr1, arr2){
          return arr1.filter(function(itemInArr1){
            return arr2.indexOf(itemInArr1) === -1;
          });
        };
    
        return funWithFiltering(acc, item).concat(funWithFiltering(item, acc));
      }. []);
    }
    

      这看起来更好。但仍然有一个问题。这将保持数组中的重复。如果这是不需要的,我们可以很容易地写另一个函数使用减少删除重复。

      

    function removeDuplicates(arr){
      arr.filter(item, index, self){
        // Keep only the first instance of the array, as given by indexOf()
        // Remove other elements from Array
        return self.indexOf(item) === index;
      }
    }
    

      我们不能再继续忽视。我一直在使用filter,而使用reduce更有远眺性,对吗?原因很简单——filter可以用reduce写的。事实上,任何数组操作,在理论上;可以实现reduce()。做给它一个尝试!用reduce的实现man和filter。你也必须照顾可选的参数。  


      结束了 

      哇,那是相当多!但我认为我已经做了一个强大的情况下,使用减少每当你想使用一个循环来完成它。要习惯它喜欢你的第一天性。一旦你得到一个问题在一些字符串或数组manipuation转化;开始写

    return arr.reduce(function(acc, item){_}, _);
    

      然后填补空白。当你使用reduce(),你在每一个元素与其他元素的相互作用方面的思考。你是acculumating从头到尾形成输出。

      框架归来拥抱减少的原则,在网页设计中获得很高的知名度。还注意到另一个显着的功能-减少力量或指导你形成你的解决方案,而不改变任何现有的。例如,在最后一个示例中;我们被过滤和连接-但我们知道这将是工作;由于操作的第一集并没有改变任何的ACC或项目内的迭代。

      初始参数是可选的,你不需要显式地提供它。如果你忽略了这个,对于第一次迭代,acc将数组中的第一项,项目将数组中的第二项。这就意味着我们可以写一笔阵效用只是忽略它。或者,我们不需要考虑-无穷大的情况下,在数组中找到最大值-它会工作得很好,如果我们删除初始值。但在一些复杂的情况下,最好是可视化和制定解决方案的一些基础-一些初始化。
      

  • 相关阅读:
    mojo 接口示例
    MojoliciousLite: 实时的web框架 概述
    接口返回json
    centos 6.7 perl 版本 This is perl 5, version 22 安装DBI DBD
    centos 6.7 perl 5.22 安装DBD 需要使用老的perl版本
    商业智能改变汽车行业
    商业智能改变汽车行业
    读MBA经历回顾(上)目的决定手段——北漂18年(48)
    perl 升级到5.20版本
    Group Commit of Binary Log
  • 原文地址:https://www.cnblogs.com/finn-Reo/p/5846509.html
Copyright © 2011-2022 走看看