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将数组中的第一项,项目将数组中的第二项。这就意味着我们可以写一笔阵效用只是忽略它。或者,我们不需要考虑-无穷大的情况下,在数组中找到最大值-它会工作得很好,如果我们删除初始值。但在一些复杂的情况下,最好是可视化和制定解决方案的一些基础-一些初始化。
      

  • 相关阅读:
    Java多线程学习
    深入理解Java之线程池
    非GUI模式下运行JMeter和远程启动JMeter
    从头写一个Cucumber测试(二) Cucumber Test
    从头写一个Cucumber测试(一) Selenium Test
    接口测试 rest-assured 使用指南
    cucumber 使用资料
    fastjson中Map与JSONObject互换,List与JOSNArray互换的实现
    json解析神器 jsonpath的使用
    【Oracle】查找每期数据都存在的产品
  • 原文地址:https://www.cnblogs.com/finn-Reo/p/5846509.html
Copyright © 2011-2022 走看看