目录
集合类
帮助函数 (打印信息)
实现
测试
检索类
运行
执行结果
拼接、附加和反转数组
添加、删除和追加值
实现 (简单实现)
扁平类
generator 类
总结
首页 web前端 js教程 通过实现25个数组方法来理解它们(收藏)

通过实现25个数组方法来理解它们(收藏)

Dec 18, 2020 am 09:21 AM
javascript 数组

本篇文章给大家介绍25个数组方法,通过实现25个数组方法来来理解及高效使用这些数组方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

通过实现25个数组方法来理解它们(收藏)

要在给定数组上使用方法,只需要通过[].方法名即可,这些方法都定义在 Array.prototype 对象上。在这里,咱们先不使用这些相,反,咱们将从简单的方法开始定义自己的版本,并在这些版本的基础上进行构建。

没有比把东西拆开再重新组装起来更好的学习方法了。注意,当咱们的实现自己的方法时,不要覆盖现有的方法,因为有的库需要它们,并且这样也方便比较咱们自己的方法与原始方法的差异。

所以不要这样命名咱们自定义的方法:

    Array.prototype.map = function map() {
     // implementation
    };
登录后复制

最好这样命名:

    function map(array) {
     // implementation
    }
登录后复制

咱们也可以通过使用class关键字并扩展Array构造函数来实现咱们的方法,如下所示:

    class OwnArray extends Array {
     public constructor(...args) {
       super(...args);
     }
    
     public map() {
       // implementation
       return this;
     }
    }
登录后复制

唯一的区别是,我们不使用数组参数,而是使用this关键字。

但是,我觉得 class 方式带来不必要的混乱,所以咱们采用第一种方法。

有了这个,咱们先从实现最简单的方法 forEach 开始!

集合类

.forEach

Array.prototype.forEach 方法对数组的每个元素执行一次提供的函数,而且不会改变原数组。

    [1, 2, 3, 4, 5].forEach(value => console.log(value));
登录后复制

实现

    function forEach(array, callback) {
      const { length } = array;
      
      for (let index = 0; index < length; index += 1) {
        const value = array[index];
        callback(value, index, array)
      }
    }
登录后复制

咱们遍历数组并为每个元素执行回调。这里需要注意的一点是,该方法没有返回什么,所以默认返回undefined

方法涟

使用数组方法的好处是可以将操作链接在一起。考虑以下代码:

    function getTodosWithCategory(todos, category) {
     return todos
       .filter(todo => todo.category === category)
       .map(todo => normalizeTodo(todo));
    }
登录后复制

这种方式,咱们就不必将map的执行结果保存到变量中,代码会更简洁。

不幸的是,forEach没有返回原数组,这意味着咱们不能做下面的事情

    // 无法工作
    function getTodosWithCategory(todos, category) {
     return todos
       .filter(todo => todo.category === category)
       .forEach((value) => console.log(value))
       .map(todo => normalizeTodo(todo));
    }
登录后复制

帮助函数 (打印信息)

接着实现一个简单的函数,它能更好地解释每个方法的功能:接受什么作为输入,返回什么,以及它是否对数组进行了修改。

    function logOperation(operationName, array, callback) {
     const input = [...array];
     const result = callback(array);
    
     console.log({
       operation: operationName,
       arrayBefore: input,
       arrayAfter: array,
       mutates: mutatesArray(input, array), // shallow check
       result,
     });
    }
登录后复制

其中 mutatesArray 方法用来判断是否更改了原数组,如果有修改刚返回 true,否则返回 false。当然大伙有好的想法可以在评论提出呦。

    function mutatesArray(firstArray, secondArray) {
      if (firstArray.length !== secondArray.length) {
        return true;
      }
    
      for (let index = 0; index < firstArray.length; index += 1) {
        if (firstArray[index] !== secondArray[index]) {
          return true;
        }
      }
    
      return false;
    }
登录后复制

然后使用logOperation来测试咱们前面自己实现的 forEach方法。

    logOperation(&#39;forEach&#39;, [1, 2, 3, 4, 5], array => forEach(array, value => console.log(value)));
登录后复制

打印结果:

    {
      operation: &#39;forEach&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: undefined
    }
登录后复制

.map

map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。

实现

    function map(array, callback) {
      const result = [];
      const { length } = array;
      
      for (let index = 0; index < length; index +=1) {
        const value = array[index];
        
        result[index] = callback(value, index, array);
      }
    
      return result;
    }
登录后复制

提供给方法的回调函数接受旧值作为参数,并返回一个新值,然后将其保存在新数组中的相同索引下,这里用变量 result 表示。

这里需要注意的是,咱们返回了一个新的数组,不修改旧的。

测试

    logOperation(&#39;map&#39;, [1, 2, 3, 4, 5], array => map(array, value => value + 5));
登录后复制

打印结果:

    { 
      operation: &#39;map&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 6, 7, 8, 9, 10 ]
     }
登录后复制

.filter

Array.prototype.filter 过滤回调返回为false的值,每个值都保存在一个新的数组中,然后返回。

    [1, 2, 3, 4, 5].filter(number => number >= 3);
    // -> [3, 4, 5]
登录后复制

实现

    function push(array, ...values) {
      const { length: arrayLength } = array;
      const { length: valuesLength } = values;
    
      for (let index = 0; index < valuesLength; index += 1) {
        array[arrayLength + index] = values[index];
      }
    
      return array.length;
    }
    --------------------------------------------------
    function filter(array, callback) {
     const result = [];
    
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         push(result, value);
       }
     }
    
     return result;
    }
登录后复制

获取每个值并检查所提供的回调函数是否返回truefalse,然后将该值添加到新创建的数组中,或者适当地丢弃它。

注意,这里对result 数组使用push方法,而不是将值保存在传入数组中放置的相同索引中。这样,result就不会因为丢弃的值而有空槽。

测试

    logOperation(&#39;filter&#39;, [1, 2, 3, 4, 5], array => filter(array, value => value >= 2));
登录后复制

运行:

    { 
      operation: &#39;filter&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 2, 3, 4, 5 ] 
    }
登录后复制

.reduce

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值reduce() 方法接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce() 的数组

确切地说,如何计算该值是需要在回调中指定的。来看呓使用reduce的一个简单的例子:对一组数字求和:

     [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, number) => {
       return sum + number;
     }, 0) // -> 55
登录后复制

注意这里的回调接受两个参数:sumnumber。第一个参数总是前一个迭代返回的结果,第二个参数在遍历中的当前数组元素。

这里,当咱们对数组进行迭代时,sum包含到循环当前索引的所有数字的和因为每次迭代咱们都将数组的当前值添加到sum中。

实现

    function reduce(array, callback, initValue) {
      const { length } = array;
      
      let acc = initValue;
      let startAtIndex = 0;
    
      if (initValue === undefined) {
        acc = array[0];
        startAtIndex = 0;
      }
    
      for (let index = startAtIndex; index < length; index += 1) {
        const value = array[index];
        acc = callback(acc, value, index, array)
      }
     
      return acc;
    }
登录后复制

咱们创建了两个变量accstartAtIndex,并用它们的默认值初始化它们,分别是参数initValue0

然后,检查initValue是否是undefined。如果是,则必须将数组的第一个值设置为初值,为了不重复计算初始元素,将startAtIndex设置为1

每次迭代,reduce方法都将回调的结果保存在累加器(acc)中,然后在下一个迭代中使用。对于第一次迭代,acc被设置为initValuearray[0]

测试

    logOperation(&#39;reduce&#39;, [1, 2, 3, 4, 5], array => reduce(array, (sum, number) => sum + number, 0));
登录后复制

运行:

    { operation: &#39;reduce&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 15 
    }
登录后复制

检索类

有什么操作比搜索特定值更常见?这里有一些方法可以帮助我们。

.findIndex

findIndex帮助咱们找到数组中给定值的索引。

    [1, 2, 3, 4, 5, 6, 7].findIndex(value => value === 5); // 4
登录后复制

findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。如果找到这样的元素,findIndex会立即返回该元素的索引。如果回调从不返回真值,或者数组的length0,则findIndex返回-1

实现

    function findIndex(array, callback) {
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         return index;
       }
     }
    
     return -1;
    }
登录后复制

测试

    logOperation(&#39;findIndex&#39;, [1, 2, 3, 4, 5], array => findIndex(array, number => number === 3));
登录后复制

运行:

    {
      operation: &#39;findIndex&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 2
    }
登录后复制

.find

findfindIndex的唯一区别在于,它返回的是实际值,而不是索引。实际工作中,咱们可以重用已经实现的findIndex

    [1, 2, 3, 4, 5, 6, 7].find(value => value === 5); // 5
登录后复制

实现

    function find(array, callback) {
     const index = findIndex(array, callback);
    
     if (index === -1) {
       return undefined;
     }
    
     return array[index];
    }
登录后复制

测试

    logOperation(&#39;find&#39;, [1, 2, 3, 4, 5], array => find(array, number => number === 3));
登录后复制

运行

    {
      operation: &#39;find&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 3
    }
登录后复制

.indexOf

indexOf是获取给定值索引的另一种方法。然而,这一次,咱们将实际值作为参数而不是函数传递。同样,为了简化实现,可以使用前面实现的findIndex

    [3, 2, 3].indexOf(3); // -> 0
登录后复制

实现

    function indexOf(array, searchedValue) {
      return findIndex(array, value => value === searchedValue)
    }
登录后复制

测试

    logOperation(&#39;indexOf&#39;, [1, 2, 3, 4, 5], array => indexOf(array, 3));
登录后复制

执行结果

    {
      operation: &#39;indexOf&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: 2
    }
登录后复制

.lastIndexOf

lastIndexOf的工作方式与indexOf相同,lastIndexOf() 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1

    [3, 2, 3].lastIndexOf(3); // -> 2
登录后复制

实现

    function lastIndexOf(array, searchedValue) {
      for (let index = array.length - 1; index > -1; index -= 1 ){
        const value = array[index];
        
        if (value === searchedValue) {
          return index;
        }
      }
      return  -1;
    }
登录后复制

代码基本与findIndex类似,但是没有执行回调,而是比较valuesearchedValue。如果比较结果为 true,则返回索引,如果找不到值,返回-1

测试

    logOperation(&#39;lastIndexOf&#39;, [1, 2, 3, 4, 5, 3], array => lastIndexOf(array, 3));
登录后复制

执行结果

    { 
      operation: &#39;lastIndexOf&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5, 3 ],
      arrayAfter: [ 1, 2, 3, 4, 5, 3 ],
      mutates: false,
      result: 5 
    }
登录后复制

.every

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值。

    [1, 2, 3].every(value => Number.isInteger(value)); // -> true
登录后复制

咱们可以将every 方法看作一个等价于逻辑与的数组。

实现

    function every(array, callback){
      const { length } = array;
      
      for (let index = 0; index < length; index += 1) {
       const value = array[index];
       
        if (!callback(value, index, array)) {
          return false;
        }
      }
    
      return true;
    }
登录后复制

咱们为每个值执行回调。如果在任何时候返回false,则退出循环,整个方法返回false。如果循环终止而没有进入到if语句里面(说明条件都成立),则方法返回true

测试

    logOperation(&#39;every&#39;, [1, 2, 3, 4, 5], array => every(array, number => Number.isInteger(number)));
登录后复制

执行结果

    {
      operation: &#39;every&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true 
    }
登录后复制

.some

some 方法与 every 刚好相反,即只要其中一个为true 就会返回true。与every 方法类似,咱们可以将some 方法看作一个等价于逻辑或数组。

    [1, 2, 3, 4, 5].some(number => number === 5); // -> true
登录后复制

实现

    function some(array, callback) {
     const { length } = array;
    
     for (let index = 0; index < length; index += 1) {
       const value = array[index];
    
       if (callback(value, index, array)) {
         return true;
       }
     }
    
     return false;
    }
登录后复制

咱们为每个值执行回调。如果在任何时候返回true,则退出循环,整个方法返回true。如果循环终止而没有进入到if语句里面(说明条件都不成立),则方法返回false

测试

    logOperation(&#39;some&#39;, [1, 2, 3, 4, 5], array => some(array, number => number === 5));
登录后复制

执行结果

    {
      operation: &#39;some&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true
    }
登录后复制

.includes

includes方法的工作方式类似于 some 方法,但是includes不用回调,而是提供一个参数值来比较元素。

    [1, 2, 3].includes(3); // -> true
登录后复制

实现

    function includes(array, searchedValue){
      return some(array, value => value === searchedValue)
    }
登录后复制

测试

    logOperation(&#39;includes&#39;, [1, 2, 3, 4, 5], array => includes(array, 5));
登录后复制

执行结果

    {
      operation: &#39;includes&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: true
    }
登录后复制

拼接、附加和反转数组

.concat

concat() 方法用于合并两个或多个数组,此方法不会更改现有数组,而是返回一个新数组。

    [1, 2, 3].concat([4, 5], 6, [7, 8]) // -> [1, 2, 3, 4, 5, 6, 7, 8]
登录后复制

实现

    function concat(array, ...values) {
      const result = [...array];
      const { length } = values;
    
      for (let index = 0; index < length; index += 1) {
        const value = values[index];
        
        if (Array.isArray(value)) {
          push(result, ...value);
        } else {
          push(result, value);
        }
      }
    
      return result;
    }
登录后复制

concat将数组作为第一个参数,并将未指定个数的值作为第二个参数,这些值可以是数组,也可以是其他类型的值。

首先,通过复制传入的数组创建 result 数组。然后,遍历 values ,检查该值是否是数组。如果是,则使用push函数将其值附加到结果数组中。

push(result, value) 只会向数组追加为一个元素。相反,通过使用展开操作符push(result,…value) 将数组的所有值附加到result 数组中。在某种程度上,咱们把数组扁平了一层。

测试

    logOperation(&#39;concat&#39;, [1, 2, 3, 4, 5], array => concat(array, 1, 2, [3, 4]));
登录后复制

执行结果

    { 
     operation: &#39;concat&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 1, 2, 3, 4, 5, 1, 2, 3, 4 ] 
    }
登录后复制

.join

join() 方法用于把数组中的所有元素放入一个字符串,元素是通过指定的分隔符进行分隔的。

    [&#39;Brian&#39;, &#39;Matt&#39;, &#39;Kate&#39;].join(&#39;, &#39;) // -> Brian, Matt, Kate
登录后复制

实现

    function join(array, joinWith) {
      return reduce(
        array,
        (result, current, index) => {
          if (index === 0) {
            return current;
          }
          
          return `${result}${joinWith}${current}`;
        },
        &#39;&#39;
      )
    }
登录后复制

reduce的回调是神奇之处:reduce遍历所提供的数组并将结果字符串拼接在一起,在数组的值之间放置所需的分隔符(作为joinWith传递)。

array[0]值需要一些特殊的处理,因为此时result是一个空字符串,而且咱们也不希望分隔符(joinWith)位于第一个元素前面。

测试

    logOperation(&#39;join&#39;, [1, 2, 3, 4, 5], array => join(array, &#39;, &#39;));
登录后复制

执行结果

    {
      operation: &#39;join&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: &#39;1, 2, 3, 4, 5&#39;
    }
登录后复制

.reverse

reverse() 方法将数组中元素的位置颠倒,并返回该数组,该方法会改变原数组。

实现

    function reverse(array) {
      const result = []
      const lastIndex = array.length - 1;
    
      for (let index = lastIndex; index > -1; index -= 1) {
        const value = array[index];
        result[lastIndex - index ] = value
      }
      return result;
    }
登录后复制

其思路很简单:首先,定义一个空数组,并将数组的最后一个索引保存为变量(lastIndex)。接着反过来遍历数组,将每个值保存在结果result 中的(lastIndex - index)位置,然后返回result数组。

测试

    logOperation(&#39;reverse&#39;, [1, 2, 3, 4, 5], array => reverse(array));
登录后复制

执行结果

    {
      operation: &#39;reverse&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 5, 4, 3, 2, 1 ]
    }
登录后复制

添加、删除和追加值

.shift

shift() 方法从数组中删除第一个元素,并返回该元素的值,此方法更改数组的长度。

    [1, 2, 3].shift(); // -> 1
登录后复制

实现

    function shift(array) {
      const { length } = array;
      const firstValue = array[0];
    
      for (let index = 1; index > length; index += 1) {
        const value = array[index];
        array[index - 1] = value;
      }
    
      array.length = length - 1;
    
      return firstValue;
    }
登录后复制

首先保存数组的原始长度及其初始值,然后遍历数组并将每个值向下移动一个索引。完成遍历后,更新数组的长度并返回初始值。

测试

    logOperation(&#39;shift&#39;, [1, 2, 3, 4, 5], array => shift(array));
登录后复制

执行结果

    {
      operation: &#39;shift&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 2, 3, 4, 5 ],
      mutates: true,
      result: 1
    }
登录后复制

.unshift

unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

    [2, 3, 4].unshift(1); // -> [1, 2, 3, 4]
登录后复制

实现

    function unshift(array, ...values) {
      const mergedArrays = concat(values, ...array);
      const { length: mergedArraysLength } = mergedArrays;
    
      for (let index = 0; index < mergedArraysLength; index += 1) {
        const value = mergedArrays[index];
        array[index] = value;
      }
    
      return array.length;
    }
登录后复制

首先将需要加入数组(作为参数传递的单个值)和数组拼接起来。这里需要注意的是values 放在第一位的,也就是放置在原始数组的前面。

然后保存这个新数组的长度并遍历它,将它的值保存在原始数组中,并覆盖开始时的值。

测试

logOperation(&#39;unshift&#39;, [1, 2, 3, 4, 5], array => unshift(array, 0));
登录后复制

执行结果

    {
      operation: &#39;unshift&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 0, 1, 2, 3, 4, 5 ],
      mutates: true,
      result: 6
    }
登录后复制

.slice

    slice()
登录后复制

方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)原始数组不会被改变。

slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。

    [1, 2, 3, 4, 5, 6, 7].slice(3, 6); // -> [4, 5, 6]
登录后复制

实现 (简单实现)

    function slice(array, startIndex = 0, endIndex = array.length) {
     const result = [];
    
     for (let index = startIndex; index < endIndex; index += 1) {
       const value = array[index];
    
       if (index < array.length) {
         push(result, value);
       }
     }
    
     return result;
    }
登录后复制

咱们遍历数组从startIndexendIndex,并将每个值放入result。这里使用了这里的默认参数,这样当没有传递参数时,slice方法只创建数组的副本。

注意:if语句确保只在原始数组中存在给定索引下的值时才加入 result 中。

测试

    logOperation(&#39;slice&#39;, [1, 2, 3, 4, 5], array => slice(array, 1, 3));
登录后复制

执行结果

    {
      operation: &#39;slice&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4, 5 ],
      mutates: false,
      result: [ 2, 3 ]
    }
登录后复制

.splice

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

首先,指定起始索引,然后指定要删除多少个值,其余的参数是要插入的值。

    const arr = [1, 2, 3, 4, 5];
    // 从位置0开始,删除2个元素后插入 3, 4, 5
    arr.splice(0, 2, 3, 4, 5);
    
    arr // -> [3, 4, 5, 3, 4, 5]
登录后复制

实现

    function splice( array, insertAtIndex, removeNumberOfElements, ...values) {
      const firstPart = slice(array, 0, insertAtIndex);
      const secondPart = slice(array, insertAtIndex + removeNumberOfElements);
    
      const removedElements = slice(
        array,
        insertAtIndex,
        insertAtIndex + removeNumberOfElements
      );
    
      const joinedParts = firstPart.concat(values, secondPart);
      const { length: joinedPartsLength } = joinedParts;
    
      for (let index = 0; index < joinedPartsLength; index += 1) {
        array[index] = joinedParts[index];
      }
    
      array.length = joinedPartsLength;
    
      return removedElements;
    }
登录后复制

其思路是在insertAtIndexinsertAtIndex + removeNumberOfElements上进行两次切割。这样,将原始数组切成三段。第一部分(firstPart)和第三部分(secondPart)加个插入的元素组成为最后数组的内容。

测试

    logOperation(&#39;splice&#39;, [1, 2, 3, 4, 5], array => splice(array, 1, 3));
登录后复制

执行结果

    {
      operation: &#39;splice&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 5 ],
      mutates: true,
      result: [ 2, 3, 4 ]
    }
登录后复制

.pop

pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。

实现

    function pop(array) {
     const value = array[array.length - 1];
    
     array.length = array.length - 1;
    
     return value;
    }
登录后复制

首先,将数组的最后一个值保存在一个变量中。然后只需将数组的长度减少1,从而删除最后一个值。

测试

    logOperation(&#39;pop&#39;, [1, 2, 3, 4, 5], array => pop(array));
登录后复制

执行结果

    {
      operation: &#39;pop&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [ 1, 2, 3, 4 ],
      mutates: true,
      result: 5
    }
登录后复制

.push

push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。

    [1, 2, 3, 4].push(5); // -> [1, 2, 3, 4, 5]
登录后复制

实现

    function push(array, ...values) {
      const { length: arrayLength } = array;
      const { length: valuesLength } = values;
    
      for (let index = 0; index < valuesLength; index += 1) {
        array[arrayLength + index] = values[index];
      }
    
      return array.length;
    }
登录后复制

首先,我们保存原始数组的长度,以及在它们各自的变量中要添加的值。然后,遍历提供的值并将它们添加到原始数组中。

测试

    logOperation(&#39;push&#39;, [1, 2, 3, 4, 5], array => push(array, 6, 7));
登录后复制

执行结果

    {
      operation: &#39;push&#39;,
      arrayBefore: [ 1, 2, 3, 4, 5 ],
      arrayAfter: [
        1, 2, 3, 4,5, 6, 7
      ],
      mutates: true,
      result: 7
    }
登录后复制

.fill

当咱们想用一个占位符值填充一个空数组时,可以使用fill方法。如果想创建一个指定数量的null元素数组,可以这样做:

    [...Array(5)].fill(null) // -> [null, null, null, null, null]
登录后复制

实现

    function fill(array, value, startIndex = 0, endIndex = array.length) {
     for (let index = startIndex; index < endIndex; index += 1) {
       array[index] = value;
     }
    
     return array;
    }
登录后复制

fill方法真正做的是替换指定索引范围内的数组的值。如果没有提供范围,该方法将替换所有数组的值。

测试

    logOperation("fill", [...new Array(5)], array => fill(array, 0));
登录后复制

执行结果

    {
      operation: &#39;fill&#39;,
      arrayBefore: [ undefined, undefined, undefined, undefined, undefined ],
      arrayAfter: [ 0, 0, 0, 0, 0 ],
      mutates: true,
      result: [ 0, 0, 0, 0, 0 ]
    }
登录后复制

扁平类

有时咱们的数组会变嵌套两到三层,咱们想要将它们扁,也就是减少嵌套的程度。例如,想将所有值都放到顶层。为咱们提供帮助有两个新特性:flatflatMap 方法。

.flat

flat方法通过可指定深度值来减少嵌套的深度。

    [1, 2, 3, [4, 5, [6, 7, [8]]]].flat(1); // -> [1, 2, 3, 4, 5, [6, 7, [8]]]
登录后复制

因为展开的深度值是1,所以只有第一级数组是被扁平,其余的保持不变。

    [1, 2, 3, [4, 5]].flat(1) // -> [1, 2, 3, 4, 5]
登录后复制

实现

    function flat(array, depth = 0) {
     if (depth < 1 || !Array.isArray(array)) {
       return array;
     }
    
     return reduce(
       array,
       (result, current) => {
         return concat(result, flat(current, depth - 1));
       },
       [],
     );
    }
登录后复制

首先,我们检查depth参数是否小于1。如果是,那就意味着没有什么要扁平的,咱们应该简单地返回数组。

其次,咱们检查数组参数是否属于数组类型,因为如果它不是,那么扁化就没有意义了,所以只返回这个参数。

咱们们使用了之前实现的reduce函数。从一个空数组开始,然后取数组的每个值并将其扁平。

注意,我们调用带有(depth - 1)flat函数。每次调用时,都递减depth参数,以免造成无限循环。扁平化完成后,将返回值来回加到result数组中。

测试

    logOperation(&#39;flat&#39;, [1, 2, 3, [4, 5, [6]]], array => flat(array, 2));
登录后复制

执行结果

    {
      operation: &#39;flat&#39;,
      arrayBefore: [ 1, 2, 3, [ 4, 5, [Array] ] ],
      arrayAfter: [ 1, 2, 3, [ 4, 5, [Array] ] ],
      mutates: false,
      result: [ 1, 2, 3, 4, 5, 6 ]
    }
登录后复制

.flatMap

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。

在上面的map方法中,对于每个值,只返回一个值。这样,一个包含三个元素的数组在映射之后仍然有三个元素。使用flatMap,在提供的回调函数中,可以返回一个数组,这个数组稍后将被扁平。

    [1, 2, 3].flatMap(value => [value, value, value]); // [1, 1, 1, 2, 2, 2, 3, 3, 3]
登录后复制

每个返回的数组都是扁平的,我们得到的不是一个嵌套了三个数组的数组,而是一个包含9个元素的数组。

实现

    function flatMap(array, callback) {
     return flat(map(array, callback), 1);
    }
登录后复制

首先使用map,然后将数组的结果数组扁平化一层。

测试

    logOperation(&#39;flatMap&#39;, [1, 2, 3], array => flatMap(array, number => [number, number]));
登录后复制

执行结果

    {
      operation: &#39;flatMap&#39;,
      arrayBefore: [ 1, 2, 3 ],
      arrayAfter: [ 1, 2, 3 ],
      mutates: false,
      result: [ 1, 1, 2, 2, 3, 3 ]
    }
登录后复制

generator 类

最后三种方法的特殊之处在于它们返回生成器的方式。如果你不熟悉生成器,请跳过它们,因为你可能不会很快使用它们。

.values

values方法返回一个生成器,该生成器生成数组的值。

    const valuesGenerator = values([1, 2, 3, 4, 5]);
    
    valuesGenerator.next(); // { value: 1, done: false }
登录后复制

实现

    function values(array) {
     const { length } = array;
    
     function* createGenerator() {
       for (let index = 0; index < length; index += 1) {
         const value = array[index];
         yield value;
       }
     }
    
     return createGenerator();
    }
登录后复制

首先,咱们定义createGenerator函数。在其中,咱们遍历数组并生成每个值。

.keys

keys方法返回一个生成器,该生成器生成数组的索引。

    const keysGenerator = keys([1, 2, 3, 4, 5]);
    
    keysGenerator.next(); // { value: 0, done: false }
登录后复制

实现

    function keys(array) {
     function* createGenerator() {
       const { length } = array;
    
       for (let index = 0; index < length; index += 1) {
         yield index;
       }
     }
    
     return createGenerator();
    }
登录后复制

实现完全相同,但这一次,生成的是索引,而不是值。

.entries

entry方法返回生成键值对的生成器。

    const entriesGenerator = entries([1, 2, 3, 4, 5]);
    
    entriesGenerator.next(); // { value: [0, 1], done: false }
登录后复制

实现

    function entries(array) {
     const { length } = array;
    
     function* createGenerator() {
       for (let index = 0; index < length; index += 1) {
         const value = array[index];
         yield [index, value];
       }
     }
    
     return createGenerator();
    }
登录后复制

同样的实现,但现在咱们将索引和值结合起来,并在数组中生成它们。

总结

高效使用数组的方法是成为一名优秀开发人员的基础。了解他们内部工作的复杂性是我所知道的最好的方法。

英文原文:https://dev.to/bnevilleoneill/understand-array-methods-by-implementing-them-all-of-them-iha

更多编程相关知识,请访问:编程教学!!

以上是通过实现25个数组方法来理解它们(收藏)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何使用 foreach 循环去除 PHP 数组中的重复元素? 如何使用 foreach 循环去除 PHP 数组中的重复元素? Apr 27, 2024 am 11:33 AM

使用foreach循环去除PHP数组中重复元素的方法如下:遍历数组,若元素已存在且当前位置不是第一个出现的位置,则删除它。举例而言,若数据库查询结果存在重复记录,可使用此方法去除,得到不含重复记录的结果。

PHP数组深度复制的艺术:使用不同方法实现完美复制 PHP数组深度复制的艺术:使用不同方法实现完美复制 May 01, 2024 pm 12:30 PM

PHP中深度复制数组的方法包括:使用json_decode和json_encode进行JSON编码和解码。使用array_map和clone进行深度复制键和值的副本。使用serialize和unserialize进行序列化和反序列化。

PHP 数组键值翻转:不同方法的性能对比分析 PHP 数组键值翻转:不同方法的性能对比分析 May 03, 2024 pm 09:03 PM

PHP数组键值翻转方法性能对比表明:array_flip()函数在大型数组(超过100万个元素)下比for循环性能更优,耗时更短。手动翻转键值的for循环方法耗时相对较长。

深度复制PHP数组的最佳实践:探索高效的方法 深度复制PHP数组的最佳实践:探索高效的方法 Apr 30, 2024 pm 03:42 PM

在PHP中执行数组深度复制的最佳实践是:使用json_decode(json_encode($arr))将数组转换为JSON字符串,然后再将其转换回数组。使用unserialize(serialize($arr))将数组序列化为字符串,然后将其反序列化为新数组。使用RecursiveIteratorIterator迭代器对多维数组进行递归遍历。

PHP数组多维排序实战:从简单到复杂场景 PHP数组多维排序实战:从简单到复杂场景 Apr 29, 2024 pm 09:12 PM

多维数组排序可分为单列排序和嵌套排序。单列排序可使用array_multisort()函数按列排序;嵌套排序需要递归函数遍历数组并排序。实战案例包括按产品名称排序和按销售量和价格复合排序。

PHP 数组分组函数在数据整理中的应用 PHP 数组分组函数在数据整理中的应用 May 04, 2024 pm 01:03 PM

PHP的array_group_by函数可根据键或闭包函数对数组中的元素分组,返回一个关联数组,其中键是组名,值是属于该组的元素数组。

PHP 数组合并去重算法:并行的解决方案 PHP 数组合并去重算法:并行的解决方案 Apr 18, 2024 pm 02:30 PM

PHP数组合并去重算法提供了并行的解决方案,将原始数组分成小块并行处理,主进程合并块的结果去重。算法步骤:分割原始数组为均等分配的小块。并行处理每个块去重。合并块结果并再次去重。

PHP 数组分组函数在查找重复元素中的作用 PHP 数组分组函数在查找重复元素中的作用 May 05, 2024 am 09:21 AM

PHP的array_group()函数可用于按指定键对数组进行分组,以查找重复元素。该函数通过以下步骤工作:使用key_callback指定分组键。可选地使用value_callback确定分组值。对分组元素进行计数并识别重复项。因此,array_group()函数对于查找和处理重复元素非常有用。

See all articles