Javascript array method, compared to the map
, filter
, forEach
and other commonly used iterative method, reduce
often ignored by us, together to explore what today reduce
we combat in development, at which it can have a magical effect, from the following reduce
syntax began to introduce.
grammar
array.reduce(function(accumulator, arrayElement, currentIndex, arr), initialValue)
If the incoming initial value, accumulator first iteration is the initial value, otherwise it is the first element of the array; in the subsequent iteration is the first iteration of the results returned by the function. Therefore, if the length of the array is n, if the incoming initial value for the n-number of iterations; otherwise n-1.
To achieve such an array arr = [1,2,3,4] and array seek
let arr = [1,2,3,4];
arr.reduce(function(pre,cur){return pre + cur}); // return 10
In fact, there are many important reduce usage because the accumulator value may not necessarily be a simple type (such as numeric or string), it can also be a structured type (such as an array or object), which allows us to use it do some other useful things, such as:
- Convert an array of object
- Expand greater array
- Calculated twice in one traversal
- The combination of mapping and filter function
- Sequential operation of the asynchronous functions
The array into an object
In the real business development, you may encounter such a situation, the array type back returned interface, you need to convert it as a target value for each lookup function as a key, according to the array id value.
E.g:
const userList = [
{
id: 1,
username: 'john',
sex: 1,
email: '[email protected]'
},
{
id: 2,
username: 'jerry',
sex: 1,
email: '[email protected]'
},
{
id: 3,
username: 'nancy',
sex: 0,
email: ''
}
];
If you used lodash the library, using _.keyBy
this method can be converted, but use reduce
can achieve such a demand.
function keyByUsernameReducer(acc, person) {
return {...acc, [person.id]: person};
}
const userObj = peopleArr.reduce(keyByUsernameReducer, {});
console.log(userObj);
The expansion into large arrays of small arrays
Imagine a scenario where we will be a bunch of plain text line read into the array, we want each line separated by commas, to generate a list of a larger array.
const fileLines = [
'Inspector Algar,Inspector Bardle,Mr. Barker,Inspector Barton',
'Inspector Baynes,Inspector Bradstreet,Inspector Sam Brown',
'Monsieur Dubugue,Birdy Edwards,Inspector Forbes,Inspector Forrester',
'Inspector Gregory,Inspector Tobias Gregson,Inspector Hill',
'Inspector Stanley Hopkins,Inspector Athelney Jones'
];
function splitLineReducer(acc, line) {
return acc.concat(line.split(/,/g));
}
const investigators = fileLines.reduce(splitLineReducer, []);
console.log(investigators);
// [
// "Inspector Algar",
// "Inspector Bardle",
// "Mr. Barker",
// "Inspector Barton",
// "Inspector Baynes",
// "Inspector Bradstreet",
// "Inspector Sam Brown",
// "Monsieur Dubugue",
// "Birdy Edwards",
// "Inspector Forbes",
// "Inspector Forrester",
// "Inspector Gregory",
// "Inspector Tobias Gregson",
// "Inspector Hill",
// "Inspector Stanley Hopkins",
// "Inspector Athelney Jones"
// ]
We start from an array of length 5, and finally get an array of length 16.
Another common situation is to increase the array of flatMap, sometimes we use the map method requires two arrays unfold, then you can use reduce flattening of the
E.g:
Array.prototype.flatMap = function(f) {
const reducer = (acc, item) => acc.concat(f(item));
return this.reduce(reducer, []);
}
const arr = ["今天天气不错", "", "早上好"]
const arr1 = arr.map(s => s.split(""))
// [["今", "天", "天", "气", "不", "错"],[""],["早", "上", "好"]]
const arr2 = arr.flatMap(s => s.split(''));
// ["今", "天", "天", "气", "不", "错", "", "早", "上", "好"]
Calculated twice in one traversal
Sometimes we need to calculate the array twice. For example, we might want to calculate the maximum and minimum numbers of the list. We can do this by twice by:
const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
const maxReading = readings.reduce((x, y) => Math.max(x, y), Number.MIN_VALUE);
const minReading = readings.reduce((x, y) => Math.min(x, y), Number.MAX_VALUE);
console.log({minReading, maxReading});
// {minReading: 0.2, maxReading: 5.5}
This requires us to traverse an array twice. However, sometimes we may not want to do that. Because .reduce () let us return any type we want, we do not have to return a number. We can combine two values encoded into an object. Then we can calculate twice at each iteration, and only once through the array:
const readings = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
function minMaxReducer(acc, reading) {
return {
minReading: Math.min(acc.minReading, reading),
maxReading: Math.max(acc.maxReading, reading),
};
}
const initMinMax = {
minReading: Number.MAX_VALUE,
maxReading: Number.MIN_VALUE,
};
const minMax = readings.reduce(minMaxReducer, initMinMax);
console.log(minMax);
// {minReading: 0.2, maxReading: 5.5}
The mapping and a process for the combined filter
Or a list of users that previously, we hope to find no e-mail address of the person's user name, user name which returns the string with a comma splice. One method is to use two separate operations:
- Get no e-mail after the entry filter
- Obtain a user name and stitching
Put them together might look like this:
function notEmptyEmail(x) {
return !!x.email
}
function notEmptyEmailUsername(a, b) {
return a ? `${a},${b.username}` : b.username
}
const userWithEmail = userList.filter(notEmptyEmail);
const userWithEmailFormatStr = userWithEmail.reduce(notEmptyEmailUsername, '');
console.log(userWithEmailFormatStr);
// 'john,jerry'
Now, this code is perfectly readable, for small sample data will not have performance problems, but if we have a vast array of it? If we modify our reducer callback, then we can do all things once:
function notEmptyEmail(x) {
return !!x.email
}
function notEmptyEmailUsername(usernameAcc, person){
return (notEmptyEmail(person))
? (usernameAcc ? `${usernameAcc},${person.username}` : `${person.username}`) : usernameAcc;
}
const userWithEmailFormatStr = userList.reduce(notEmptyEmailUsername, '');
console.log(userWithEmailFormatStr);
// 'john,jerry'
In this version, we only traversed once the array is generally recommended to use filter
and map
combination, unless found performance problems, it is recommended reduce
to do optimization.
Sequential operation of the asynchronous functions
Another thing we can do .reduce () is run sequentially promises (rather than parallel). If you have a request for the API rate limit, or you need to pass the results of each prmise to the next promise, reduce
we can help you.
As an example, suppose we want to userList
get the message that everyone in the array.
function fetchMessages(username) {
return fetch(`https://example.com/api/messages/${username}`)
.then(response => response.json());
}
function getUsername(person) {
return person.username;
}
async function chainedFetchMessages(p, username) {
// In this function, p is a promise. We wait for it to finish,
// then run fetchMessages().
const obj = await p;
const data = await fetchMessages(username);
return { ...obj, [username]: data};
}
const msgObj = userList
.map(getUsername)
.reduce(chainedFetchMessages, Promise.resolve({}))
.then(console.log);
// {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
async
The function returns a Promise object that can be used then
to add callback method. When the function is executed, the event await
will return to the first, until the asynchronous operation is complete, followed by execution statement after the function in vivo.
Please note that here we pass Promise as an initial value Promise.resolve()
, our first API call will run immediately.
The following are not using async
syntactic sugar version
function fetchMessages(username) {
return fetch(`https://example.com/api/messages/${username}`)
.then(response => response.json());
}
function getUsername(person) {
return person.username;
}
function chainedFetchMessages(p, username) {
// In this function, p is a promise. We wait for it to finish,
// then run fetchMessages().
return p.then((obj)=>{
return fetchMessages(username).then(data=>{
return {
...obj,
[username]: data
}
})
})
}
const msgObj = peopleArr
.map(getUsername)
.reduce(chainedFetchMessages, Promise.resolve({}))
.then(console.log);
// {glestrade: [ … ], mholmes: [ … ], iadler: [ … ]}
PS: more front-end information technology dry goods, please pay attention to the public number " front-end New Horizons "