Magical Javascript array in the method of the reduce

arrary.png

Javascript array method, compared to the map, filter, forEachand other commonly used iterative method, reduceoften ignored by us, together to explore what today reducewe combat in development, at which it can have a magical effect, from the following reducesyntax 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 _.keyBythis method can be converted, but use reducecan 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 filterand mapcombination, unless found performance problems, it is recommended reduceto 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, reducewe can help you.

As an example, suppose we want to userListget 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: [ … ]}

asyncThe function returns a Promise object that can be used thento add callback method. When the function is executed, the event awaitwill 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 asyncsyntactic 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 "

New Horizons front end

Guess you like

Origin www.cnblogs.com/alex1504/p/10993538.html