# Array.prototype.reduce()

`reduce()` 方法將一個累加器及陣列中每項元素（由左至右）傳入回呼函式，將陣列化為單一值。

## 語法

`arr.reduce(callback[accumulator, currentValue, currentIndex, array], initialValue)`

### 參數

`callback`

`accumulator`

`currentValue`

`currentIndex`選擇性

`array`選擇性

`initialValue`選擇性

## 描述

`reduce()` 會對每一個目前迭代到的陣列元素（除了空值以外）執行 `callback` 函式，回呼函式會接收四個參數：

• `accumulator`
• `currentValue`
• `currentIndex`
• `array`

```var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );

// reduce() without initialValue
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 }            ].reduce( maxCallback ); // { x: 22 }
[                      ].reduce( maxCallback ); // TypeError

// map/reduce; better solution, also works for empty or larger arrays
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
```

### reduce() 如何運作

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

`callback` `accumulator` `currentValue` `currentIndex` `array` return value
first call `0` `1` `1` `[0, 1, 2, 3, 4]` `1`
second call `1` `2` `2` `[0, 1, 2, 3, 4]` `3`
third call `3` `3` `3` `[0, 1, 2, 3, 4]` `6`
fourth call `6` `4` `4` `[0, 1, 2, 3, 4]` `10`

`reduce()` 的最終回傳值將會是最後一次呼叫回呼函式的回傳值 (`10`)。

```[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );
```

```[0, 1, 2, 3, 4].reduce(
(`accumulator`, currentValue, currentIndex, array) => {
return `accumulator` + currentValue;
},
10
);
```
`callback` `accumulator` `currentValue` `currentIndex` `array` return value
first call `10` `0` `0` `[0, 1, 2, 3, 4]` `10`
second call `10` `1` `1` `[0, 1, 2, 3, 4]` `11`
third call `11` `2` `2` `[0, 1, 2, 3, 4]` `13`
fourth call `13` `3` `3` `[0, 1, 2, 3, 4]` `16`
fifth call `16` `4` `4` `[0, 1, 2, 3, 4]` `20`

`reduce()` 執行的結果將會是 `20`

## 範例

### 加總所有陣例之元素值

```var sum = [0, 1, 2, 3].reduce(function (a, b) {
return a + b;
}, 0);
// sum is 6
```

```var total = [ 0, 1, 2, 3 ].reduce(
( acc, cur ) => acc + cur,
0
);```

### 攤平一個多維陣列

```var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
function(a, b) {
return a.concat(b);
},
[]
);
// flattened is [0, 1, 2, 3, 4, 5]
```

```var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( acc, cur ) => acc.concat(cur),
[]
);
```

### 計算相同元素數量並以物件鍵值顯示

```var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
```

```// friends - an array of objects
// where object field "books" - list of favorite books
var friends = [{
name: 'Anna',
books: ['Bible', 'Harry Potter'],
age: 21
}, {
name: 'Bob',
books: ['War and peace', 'Romeo and Juliet'],
age: 26
}, {
name: 'Alice',
books: ['The Lord of the Rings', 'The Shining'],
age: 18
}];

// allbooks - list which will contain all friends' books +
// additional list contained in initialValue
var allbooks = friends.reduce(function(prev, curr) {
return [...prev, ...curr.books];
}, ['Alphabet']);

// allbooks = [
//   'Alphabet', 'Bible', 'Harry Potter', 'War and peace',
//   'Romeo and Juliet', 'The Lord of the Rings',
//   'The Shining'
// ]```

### 移除陣列中的重複項目

```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]
```

### 序列執行 Promise

```/**
* Runs promises from promise array in chained manner
*
* @param {array} arr - promise arr
* @return {Object} promise object
*/
function runPromiseInSequense(arr) {
return arr.reduce((promiseChain, currentPromise) => {
return promiseChain.then((chainedResult) => {
return currentPromise(chainedResult)
.then((res) => res)
})
}, Promise.resolve());
}

// promise function 1
function p1() {
return new Promise((resolve, reject) => {
resolve(5);
});
}

// promise function 2
function p2(a) {
return new Promise((resolve, reject) => {
resolve(a * 2);
});
}

// promise function 3
function p3(a) {
return new Promise((resolve, reject) => {
resolve(a * 3);
});
}

const promiseArr = [p1, p2, p3];
runPromiseInSequense(promiseArr)
.then((res) => {
console.log(res);   // 30
});

```

## Polyfill

```// Production steps of ECMA-262, Edition 5, 15.4.4.21
// Reference: http://es5.github.io/#x15.4.4.21
// https://tc39.github.io/ecma262/#sec-array.prototype.reduce
if (!Array.prototype.reduce) {
Object.defineProperty(Array.prototype, 'reduce', {
value: function(callback /*, initialValue*/) {
if (this === null) {
throw new TypeError( 'Array.prototype.reduce ' +
'called on null or undefined' );
}
if (typeof callback !== 'function') {
throw new TypeError( callback +
' is not a function');
}

// 1. Let O be ? ToObject(this value).
var o = Object(this);

// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;

// Steps 3, 4, 5, 6, 7
var k = 0;
var value;

if (arguments.length >= 2) {
value = arguments[1];
} else {
while (k < len && !(k in o)) {
k++;
}

// 3. If len is 0 and initialValue is not present,
//    throw a TypeError exception.
if (k >= len) {
throw new TypeError( 'Reduce of empty array ' +
'with no initial value' );
}
value = o[k++];
}

// 8. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kPresent be ? HasProperty(O, Pk).
// c. If kPresent is true, then
//    i.  Let kValue be ? Get(O, Pk).
//    ii. Let accumulator be ? Call(
//          callbackfn, undefined,
//          « accumulator, kValue, k, O »).
if (k in o) {
value = callback(value, o[k], k, o);
}

// d. Increase k by 1.
k++;
}

// 9. Return accumulator.
return value;
}
});
}
```

## 規範

ECMAScript 5.1 (ECMA-262)
The definition of 'Array.prototype.reduce()' in that specification.
Standard Initial definition. Implemented in JavaScript 1.8.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Array.prototype.reduce()' in that specification.
Standard
ECMAScript (ECMA-262)
The definition of 'Array.prototype.reduce()' in that specification.
Living Standard

## 瀏覽器相容性

Update compatibility data on GitHub
Desktop Mobile Server Chrome Edge Firefox Internet Explorer Opera Safari Android webview Chrome for Android Firefox for Android Opera for Android Chrome Full support 3 Edge Full support 12 Firefox Full support 3 IE Full support 9 Opera Full support 10.5 Safari Full support 5 WebView Android Full support ≤37 Chrome Android Full support 18 Firefox Android Full support 4 Opera Android Full support 14 Safari iOS Full support 4 Samsung Internet Android Full support 1.0 nodejs Full support 0.1.100

Full support
Full support