Array高階函式reduce&filter

wq93發表於2019-03-10

① reduce()方法

1、語法

arr.reduce(callback,[initialValue])
複製程式碼

reduce 為陣列中的每一個元素依次執行回撥函式,不包括陣列中被刪除或從未被賦值的元素,接受四個引數:初始值(或者上一次回撥函式的返回值),當前元素值,當前索引,呼叫 reduce 的陣列。

callback (執行陣列中每個值的函式,包含四個引數)

    1、previousValue (上一次呼叫回撥返回的值(沒有提供initialValue),或者是提供的初始值(initialValue))
    2、currentValue (陣列中當前被處理的元素)
    3、index (當前元素在陣列中的索引)
    4、array (呼叫 reduce 的陣列)

initialValue (作為第一次呼叫 callback 的第一個引數。)
複製程式碼

2、例項解析 initialValue 引數

  1. 沒有提供initialValue引數

    var arr = [1, 2, 3, 4];
    var sum = arr.reduce(function(prev, cur, index, arr) {
        console.log(prev, cur, index);
        return prev + cur;
    })
    console.log(arr, sum);
    // 列印結果: 
    // 1 2 1
    // 3 3 2
    // 6 4 3 
    // [1, 2, 3, 4, 5] 15
    複製程式碼

    這裡可以看出,上面的例子index是從1開始的,第一次的prev的值是陣列的第一個值。陣列長度是4,但是reduce函式迴圈3次。

  2. 提供initialValue引數

    var  arr = [1, 2, 3, 4];
    var sum = arr.reduce(function(prev, cur, index, arr) {
        console.log(prev, cur, index);
        return prev + cur;
    },0) //注意這裡設定了初始值
    console.log(arr, sum);
    
     // 列印結果: 
     // 0 1 0
     // 1 2 1
     // 3 3 2 
     // 6 4 3
     // [1, 2, 3, 4] 10
    複製程式碼

    這個例子index是從0開始的,第一次的prev的值是我們設定的初始值0,陣列長度是4,reduce函式迴圈4次。

結論:如果沒有提供initialValue,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供initialValue,從索引0開始。

注意:如果這個陣列為空,運用reduce是什麼情況?

var  arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
})
//報錯,"TypeError: Reduce of empty array with no initial value"
複製程式碼

但是要是我們設定了初始值就不會報錯,如下:

var  arr = [];
var sum = arr.reduce(function(prev, cur, index, arr) {
    console.log(prev, cur, index);
    return prev + cur;
},0)
console.log(arr, sum); // [] 0
複製程式碼

所以一般來說我們提供初始值通常更安全

3、reduce的高階用法

  1. 計算陣列中每個元素出現的次數

    let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
    
    let nameNum = names.reduce((pre,cur)=>{
      if(cur in pre){
        pre[cur]++
      }else{
        pre[cur] = 1 
      }
      return pre
    },{})
    console.log(nameNum); //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
    複製程式碼
  2. 陣列去重

    let arr = [1,2,3,4,4,1]
    let newArr = arr.reduce((pre,cur)=>{
        if(!pre.includes(cur)){
          return pre.concat(cur)
        }else{
          return pre
        }
    },[])
    console.log(newArr);// [1, 2, 3, 4]
    //-----------------------------------------------
    let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
    let result = arr.sort().reduce((init, current)=>{
        if(init.length===0 || init[init.length-1]!==current){
            init.push(current);
        }
        return init;
    }, []);
    console.log(result); //[1,2,3,4,5]
    
    // 不能使用	pre.push(cur) 因為陣列的push方法返回的是新陣列的長度,而concat返回的是新陣列
    複製程式碼
  3. 將二維陣列轉化為一維

    let arr = [[0, 1], [2, 3], [4, 5]]
    let newArr = arr.reduce((pre,cur)=>{
        return pre.concat(cur)
    },[])
    console.log(newArr); // [0, 1, 2, 3, 4, 5]
    複製程式碼
  4. 將多維陣列轉化為一維

    let arr = [[0, 1], [2, 3], [4,[5,6,7]]]
    const newArr = function(arr){
       return arr.reduce((pre,cur)=>pre.concat(Array.isArray(cur)?newArr(cur):cur),[])
    }
    console.log(newArr(arr)); //[0, 1, 2, 3, 4, 5, 6, 7]
    複製程式碼
  5. 物件裡的屬性求和

    var result = [
        {
            subject: 'math',
            score: 10
        },
        {
            subject: 'chinese',
            score: 20
        },
        {
            subject: 'english',
            score: 30
        }
    ];
    
    var sum = result.reduce(function (prev, cur) {
        return cur.score + prev;
    }, 0);
    console.log(sum) //60
    複製程式碼
  6. 按屬性對object分類

    var people = [
        { name: 'Alice', age: 21 },
        { name: 'Max', age: 20 },
        { name: 'Jane', age: 20 }
    ];
    
    function groupBy(objectArray, property) {
        return objectArray.reduce(function (acc, obj) {
            console.log(obj)
            var key = obj[property];
            if (!acc[key]) {
                acc[key] = [];
            }
            acc[key].push(obj);
            return acc;
        }, {});
    }
    
    var groupedPeople = groupBy(people, 'age');
    
    // { 
    //   20: [
    //     { name: 'Max', age: 20 }, 
    //     { name: 'Jane', age: 20 }
    //   ], 
    //   21: [{ name: 'Alice', age: 21 }] 
    // }
    複製程式碼
  7. 功能型函式管道

    // Building-blocks to use for composition
    const double = x => x + x;
    const triple = x => 3 * x;
    const quadruple = x => 4 * x;
    
    // Function composition enabling pipe functionality
    const pipe = (...functions) => input => functions.reduce(
        (acc, fn) => fn(acc),
        input
    );
    
    // 還原函式
    // function pipe(...functions) {
    //     return function (input) {
    //         return functions.reduce(function (acc, fn) {
    //             return fn(acc)
    //         }, input)
    //     }
    // }
    
    // Composed functions for multiplication of specific values
    const multiply6 = pipe(double, triple);
    const multiply9 = pipe(triple, triple);
    const multiply16 = pipe(quadruple, quadruple);
    const multiply24 = pipe(double, triple, quadruple);
    
    // Usage
    multiply6(6); // 36
    multiply9(9); // 81
    multiply16(16); // 256
    multiply24(10); // 240
    複製程式碼

② filter()方法

filter 為陣列中的每個元素呼叫一次 callback 函式,並利用所有使得 callback 返回 true 或 等價於 true 的值 的元素建立一個新陣列。callback 只會在已經賦值的索引上被呼叫,對於那些已經被刪除或者從未被賦值的索引不會被呼叫。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新陣列中。

1、引數

callback

用來測試陣列的每個元素的函式。呼叫時使用引數 (element, index, array)。
複製程式碼

返回true表示保留該元素(通過測試),false則不保留。它接受三個引數:

**element**

	當前在陣列中處理的元素。

**index**可選

	正在處理元素在陣列中的索引。

**array**可選

	呼叫了`filter`的陣列。
複製程式碼

thisArg可選

可選。執行 `callback` 時的用於 `this` 的值。
複製程式碼

2、返回值

一個新的通過測試的元素的集合的陣列,如果沒有通過測試則返回空陣列

3、返回值

filter 為陣列中的每個元素呼叫一次 callback 函式,並利用所有使得 callback 返回 true 或 等價於 true 的值 的元素建立一個新陣列。callback 只會在已經賦值的索引上被呼叫,對於那些已經被刪除或者從未被賦值的索引不會被呼叫。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新陣列中。

callback 被呼叫時傳入三個引數:

  1. 元素的值
  2. 元素的索引
  3. 被遍歷的陣列

如果為 filter 提供一個 thisArg 引數,則它會被作為 callback 被呼叫時的 this 值。否則,callbackthis 值在非嚴格模式下將是全域性物件,嚴格模式下為 undefinedcallback 最終觀察到的this值是根據通常函式所看到的 "this"的規則確定的。

filter 不會改變原陣列,它返回過濾後的新陣列。

filter 遍歷的元素範圍在第一次呼叫 callback 之前就已經確定了。在呼叫 filter 之後被新增到陣列中的元素不會被 filter 遍歷到。如果已經存在的元素被改變了,則他們傳入 callback 的值是 filter 遍歷到它們那一刻的值。被刪除或從來未被賦值的元素不會被遍歷到。

4、示例

  • 篩選排除所有的小值
function isBigEnough(element) {
  return element >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered is [12, 130, 44]
複製程式碼
  • 過濾JSON中的無效條目
var arr = [
  { id: 15 },
  { id: -1 },
  { id: 0 },
  { id: 3 },
  { id: 12.2 },
  { },
  { id: null },
  { id: NaN },
  { id: 'undefined' }
];

var invalidEntries = 0;

function isNumber(obj) {
  return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
}

function filterByID(item) {
  if (isNumber(item.id) && item.id !== 0) {
    return true;
  } 
  invalidEntries++;
  return false; 
}

var arrByID = arr.filter(filterByID);

console.log('Filtered Array\n', arrByID); 
// Filtered Array
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]

console.log('Number of Invalid Entries = ', invalidEntries); 
// Number of Invalid Entries = 5
複製程式碼
  • 在陣列中搜尋
var fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];

/**
 * Array filters items based on search criteria (query)
 */
function filterItems(query) {
  return fruits.filter(function(el) {
      return el.toLowerCase().indexOf(query.toLowerCase()) > -1;
  })
}

console.log(filterItems('ap')); // ['apple', 'grapes']
console.log(filterItems('an')); // ['banana', 'mango', 'orange']
複製程式碼

  • GitHub: 歡迎Star wq93

相關文章