Powerful array

In the daily development of the front-end, arrays are used very frequently. After fully grasping various common methods of arrays, work efficiency can

Powerful array

1 Basic grammar

An array is a high-level object similar to a list. One of the JavaScript standard built-in objects, Array object, is used to construct an array.

There are three ways to create an array:

//The first literal
var arr0 = [element0, element1, ..., elementN]
//The second type of constructor
var arr1 = new Array(element0, element1[, ...[, elementN]])
//The third type of constructor
var arr2 = new Array(arrayLength)

Parameter Description:

elementN

The Array constructor will create a JavaScript array based on the given elements, except when there is only one parameter and it is a number (see the arrayLength parameter below for details). Note that the latter case only applies to arrays created with the Array constructor, not to array literals created with square brackets.

arrayLength

An integer ranging from 0 to 232-1. At this time, an array object whose length value is equal to arrayLength will be returned (the implication is that the array does not contain any actual elements at this time, and it cannot be taken for granted that it contains arrayLength values Are undefined elements). If the parameter passed in is not a valid value, a RangeError exception will be thrown.

  1. Iterate over the array

When facing an array, we often need to traverse it, so that we can conveniently operate on each element of the face. Before starting the formal content, let's take a look at the ways in which arrays can be traversed.

First of all, I think of the for loop, which can easily operate on all elements by declaring a variable as a subscript. Speaking of loops, other loops, such as when are of course no problem; we can also use recursion to traverse through the iteration of subscripts. When we look at the Array itself, we will find that the forEach and map methods provided for us on its prototype chain can also traverse the array.

So below, let's analyze three of the methods mentioned above: for, forEach, and map.

const arr = [0,1,2,3,4,5,6,7,8,9]

// for loop
// The first expression i in the brackets is the iteration variable, the second expression is the loop condition, and the third expression updates the iteration variable

for(let i = 0; i <arr.length; i ++) {...}

// forEach traverse
// A callback function must be passed in as the first parameter. The callback function accepts multiple parameters. The first parameter is the element to which the current array is traversed.

arr.forEach(item => {...})

// map traversal
// A callback function must be passed in as the first parameter. The callback function accepts multiple parameters. The first parameter is the element to which the current array is traversed.
arr.map(item => {...})

From the above code, we can find that in addition to the for loop, the other two types of traversal seem to be similar in usage. Is it possible to use the two in common? Is there any difference between them? Let's analyze the similarities and differences of the three traversal methods.

2.1 for

The traversal method of the for loop is the biggest difference from the other two. The loop is executed through the code block. In the code block, you need to get the current traversed element through iterating variables, such as arr[i].

It seems that there are no other two ways to get elements through iteration variables (you can get them directly), but in some cases, we have to use a for loop: when the loop meets certain conditions, it jumps out of the loop body or jumps out of this loop Go directly to the next cycle.

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i <arr.length; i++) {
// When the iteration variable i == 3, skip this loop and go directly to the next
if (i == 3) continue;
console.log(arr[i]);
// When i> 7, jump out of the loop
if (i> 7) break;
}
//>> 0
//>> 1
//>> 2
//>> 4
//>> 5
//>> 6
//>> 7
//>> 8

The other two traversal methods operate on the traversed elements through the callback function. Even if the return is in the callback function, it can only jump out of the current callback function, and cannot prevent the pause of the traversal itself.

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.forEach(item => {
console.log(item);
if (item> 3) return; // traversal does not end when it is greater than 3
});
//>> 0
//>> 1
//>> 2
//>> 3
//>> 4
//>> 5
//>> 6
//>> 7
//>> 8
//>> 9

2.2 forEach

The forEach() method executes the provided callback function once for each item of the array.

The syntax is as follows:

arr.forEach(callback[, thisArg]);

Parameter Description callback is a function executed by each element in the array. The function receives three parameters:

  • The current element is being processed in the currentValue array.

  • index is optional, the index of the current element being processed in the array.

  • Array is optional, the array being operated by the forEach() method.

thisArg is an optional parameter. Used as the value of this (reference object) when executing the callback function.

Since this point of the anonymous function is always the global window object, but in some cases, we need to change the this point. At this time, the role of thisArg parameter becomes prominent.

var a = "coffe";
var b = {a:"1989"};
(function() {
let arr = [0, 1, 2];
arr.forEach(function(item){
console.log(this.a);//Here is the visited b.a
},b);//After passing b as the thisArg parameter, this points to b
})();
//>> 1989
//>> 1989
//>> 1989

Note: If an arrow function expression is used to pass in the thisArg parameter, it will be ignored, because the arrow function binds the this value lexically.

var a = "coffe";
var b = {a:"1989"};
(function() {
let arr = [0, 1, 2];
arr.forEach((item)=>{
console.log(this.a);//Here is the window.a visited
},b);//After passing b as the thisArg parameter, this should point to b, but because of the arrow function expression,
//this always points to the this of the outer scope of the function that contains it (that is, the anonymous function), which is window
})();
//>> coffe
//>> coffe
//>> coffe

2.3 map

The use of map is almost the same as forEach. The only difference is that map will return a new array, and the elements of this array are the return value of the callback function, so we can use a variable to receive the return value of map.

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const arr1 = arr.map(item => item + 1);
console.log(arr1);
//>> [1,2,3,4,5,6,7,8,9,10]

const arr2 = arr.map(item => {item + 1});//Note that this callback arrow function does not return a value
console.log(arr2);

//Output an array whose items are all undefined
//>> [undefined, undefined, …… undefined]

In the above code, arr1 uses the return value item + 1 of the callback function as an element in the array, and arr2 creates an array with undefined each item because the callback function does not return a value.

2.4 for...in 与for...of

For...in traverses the index of the array item, and for...of traverses the value of the array item. for...of traverses only the items in the array, not the prototype properties, methods, and indexes of the array.

Array.prototype.getLength = function () {
   return this.length;
}
var arr = [1, 2, 4, 5, 6, 7]
arr.name = "coffe1981";
console.log("-------for...of--------");
for (var value of arr) {
   console.log(value);
}
console.log("-------for...in--------");
for (var key in arr) {
   console.log(key);
}

//>>    -------for...of--------
//>>    1
//>>    2
//>>    4
//>>    5
//>>    6
//>>    7
//>>    -------for...in--------
//>>    0
//>>    1
//>>    2
//>>    3
//>>    4
//>>    5
//>>    name
//>>    getLength

In the above code, you will find that for...in can traverse to the properties and methods of the prototype. If you don't want to traverse the properties and methods of the prototype, you can use the hasOwnPropery method inside the loop to determine whether a property is an instance property of the object.

Array.prototype.getLength = function () {
   return this.length;
}
var arr = [1, 2, 4, 5, 6, 7]
arr.name = "coffe1981";
console.log("-------for...in--------");
for (var key in arr) {
   if(arr.hasOwnProperty(key))
      console.log(key);
}
//>>    -------for...in--------
//>>    0
//>>    1
//>>    2
//>>    3
//>>    4
//>>    5
//>>    name

to sum up:

for..of is suitable for traversing arrays/array-like objects/strings/map/set and other collections with iterator objects, but it cannot traverse objects because there is no iterator object. Traverse objects usually use for...in to traverse the key name of the object. Unlike forEach, both for...of and for...in can correctly respond to break, continue, and return statements.

2.5 Filtering method filter

The filter() method returns a new array that contains all the array items that passed the callback function test.

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

Parameter Description: the callback is a function used to test each element of the array. Return true to indicate that the element passed the test and retain the element, false to not retain it. It accepts the following three parameters:

  • The element currently being processed in the element array.

  • index is optional, the index of the element being processed in the array.

  • Array is optional, the array itself that calls filter.

thisArg is an optional parameter. When executing callback, it is used to specify the value of this.

const arr1 = [1, 4, 5, 6, 2, 3, 8, 9, 0];

const arr2 = arr1.filter((item, index, array) => {
  return item > 5;
});
console.log(arr2);//>> [6, 8, 9]

In the above code, as long as the value of the current array item is greater than 5, item> 5 will return true, and it will pass the test of the callback function to retain the array item. Therefore, the new array returned after filtering the original array is [6, 8, 9].

2.6 Find method find

The find() method returns the value of the first array item in the array that passes the callback function test or is undefined if it fails the test.

The syntax is as follows:

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const value = arr.find((item, index, array) => {
    return item > 5
});
console.log(value); //>> 6

It should be noted that once the callback function test passes (returns true), the find method will immediately return the value of the current array item; if there is no array item that meets the rules, it will return undefined. The method similar to find is the findIndex() method. The difference is that find returns the value of the element, while findIndex returns the index (index) of the array item.

2.7 some

The some() method tests whether an array item in the array has passed the callback function test and returns a Boolean value.

The syntax is as follows:

arr.some(callback(element[, index[, array]])[, thisArg])

The parameter description is consistent with the filter above.

Note: If you test with an empty array, it will return false in any case.

The sample code is as follows:

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const isTrue = arr.some((item, index, array) => {
  return item > 5;
});
console.log(isTrue); //>> true

Compared with fitler and find, in addition to the difference in return value, there is another difference: if there is a deleted or unassigned index in the original array, the callback function will not be called on the array item. Isn't it a bit confusing? Look at a code example to make it clear.

const arr = new Array(4);
arr[0] = 1;
const isTrue = arr.some((item) => {
    return item > 5;
});
console.log(isTrue); //>> false

In the above example, although arr.length is 4, the callback function is only called on the item whose index is 0. Since the following three items have not been assigned, the callback function is not called.

2.8 sort The sort() method uses the in-place algorithm to sort the array items and returns the array. The default sort order is to convert the array items into strings, and then compare their UTF-16 code unit values.

In-place algorithm (in-place algorithm) is an algorithm that uses a small, fixed amount of additional space to convert data, and does not gradually expand the occupation of the additional space as the algorithm is executed. When the algorithm is executed, the input data is usually overwritten by the output part.

The syntax is as follows:

var newArray = arr.sort ([compareFunction]);

Parameter Description:

compareFunction is optional and is used to specify functions to be arranged in a certain order. If omitted, the elements are sorted according to the Unicode position of each character in the converted string.

  • firstEl The first element to be compared.

  • secondEl The second element used for comparison.

The sample code is as follows:

let arr = [1, 4, 5, 6, 2, 3, 8, 9, 0];
arr.sort((a, b) => {
  return a - b;
});
console.log(arr);
//>> [0, 1, 2, 3, 4, 5, 6, 8, 9]

The sort method receives a callback function for comparison. This function has two parameters, which represent the two items in the array to be compared. At the same time, the two array items will be sorted according to the return value of the callback function:

  • If the return value is less than 0, a will be sorted before b;

  • If the return value is greater than 0, b will be sorted before a;

  • If they are equal, the relative positions of a and b remain unchanged.

For ascending sorting of numbers, you can write a callback function like this:

(a, b) => {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
}

Simplify the above code, it will become return a-b. So for the arrangement of numbers, ascending order returns return a-b, and descending order returns return b-a. Since a and b in the callback function are respectively the two array items to be compared, if the array items are of object type, they can also be sorted by the properties of the object.

note: If the sort method does not pass the comparison callback function, then it will sort the characters according to the Unicode position.

let arr = [2, 40, 11, 5, 10];
console.log(arr.sort());
//>> [10, 11, 2, 40, 5]

In the above example, sort does not pass in the comparison callback function. It will sort according to the first character of each array item. Since 1 is before 2 in Unicode, 10 will be sorted before 2 instead of according to Compare the size of the numbers 10 and 2. If the first character of the two array items is the same, the order is compared according to the second character.

2.8 reduce

The reduce() method executes a callback function provided by you on each item in the array, and returns the result as a single value.

The syntax is as follows:

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

Parameter Description: the callback is a function that executes each value in the array and contains four parameters:

  • accumulator **accumulator, accumulates the return value of the callback. It is the cumulative value returned the last time the callback was called, or initialValue (see below).

  • The element is being processed in the current value array.

  • currentIndex is optional, the index of the current element being processed in the array. If initialValue is provided, the starting index number is 0, otherwise, it is 1.

  • array Optional call reduce() array

initialValue is optional as the value of the first parameter when the callback function is called for the first time. If the initial value is not provided, the first element in the array will be used (that is, the arr loop calculation for the array is one less time, so please pay attention to this). Calling reduce on an empty array with no initial value will report an error.

The definition of reduce is relatively abstract, and it is relatively less used in normal development, but if it is used well, it can greatly improve work efficiency, so here we focus on several common usage examples.

2.8.1 Convert an array type to an object

We can use reduce() to convert an array into an object. If you want to do query and classification, this method will be very useful. As an example, imagine we have the following peopleArr array:

const arr  = [
    {
        username:    'bobo1',
        displayname: '波仔1',
        email:       'bobo@coffe1989.com'
    },
    {
        username:    'bobo2',
        displayname: '波仔2',
        email:       'xiaobo@coffe1989.com'
    },
    {
        username:    'bobo3',
        displayname: '波仔3',
        email:       null
    },
];

In some cases, we need to query detailed people details by username. Usually, for the convenience of querying, we need to convert array to object. Then, by using the reduce() method, we can use the following method:

function callback(acc, person) {

//The following sentence uses the spread operator...acc, which means that the attributes of the acc object are "dismembered" together with the new attributes

//Return as a new object
return {...acc, [person.username]: person};
}
const obj = arr.reduce(callback, {});//The initial value here is {}
console.log(obj);

//>> {
// "bobo1": {
// "username": "bobo1",
// "displayname": "波仔1",
// "email": "bobo@coffe1989.com"
// },
// "bobo2": {
// "username": "bobo2",
// "displayname": "波仔2",
// "email": "xiaobo@coffe1989.com"
// },
// "bobo3":{
// "username": "bobo3",
// "displayname": "波仔3",
// "email": null
//}
//}

2.8.2 Expand a large array Usually, we will think that reduuce () is used to streamline a set of data to get a simpler result, which is of course an array. Since it has never been disclosed it clearly, this result (array) must be smaller than the original array length. Therefore, we can use reduuce() to convert a shorter array into a longer array. This method is useful when you need to read data from a text file. The following is an example. Suppose we have read a series of simple text data and then put into an array. Our needs are divided by commas, then get a big name list.

const arr = [
  "Algar,Bardle,Mr. Barker,Barton",
  "Baynes,Bradstreet,Sam Brown",
  "Monsieur Dubugue,Birdy Edwards,Forbes,Forrester",
  "Gregory,Tobias Gregson,Hill",
  "Stanley Hopkins,Athelney Jones"
];

function callback(acc, line) {
  return acc.concat(line.split(/,/g));
}
const arr1 = arr.reduce(callback, []);
console.log(arr1);
//>> [
//   "Algar",
//   "Bardle",
//   "Mr. Barker",
//   "Barton",
//   "Baynes",
//   "Bradstreet",
//   "Sam Brown",
//   "Monsieur Dubugue",
//   "Birdy Edwards",
//   "Forbes",
//   "Forrester",
//   "Gregory",
//   "Tobias Gregson",
//   "Hill",
//   "Stanley Hopkins",
//   "Athelney Jones"
// ]

The above code puts an array of Length 5, which is expanded into an array of Length 16.

2.8.3 Complete two calculations on array, but only once

Sometimes we need two operations for a simple array. For example, calculate the maximum and minimum value in a set of numbers. Usually we use this way of traversing twice:

const arr = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
const maxReading = arr.reduce((x, y) => Math.max(x, y), Number.MIN_VALUE);
const minReading = arr.reduce((x, y) => Math.min(x, y), Number.MAX_VALUE);
console.log({minReading, maxReading});
//>> {minReading: 0.2, maxReading: 5.5}

This method needs to traverse two arrays. However, there is a way to have so many ways to traverse. Since the reduuce () method, you can return a variety of what we need. We can put two values ​​into an object. This way we can only be used to calculate two times. code show as below:

const arr = [0.3, 1.2, 3.4, 0.2, 3.2, 5.5, 0.4];
function callback(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 result = arr.reduce(callback, initMinMax);
console.log(result);
//>> {minReading: 0.2, maxReading: 5.5}

2.8.4 In a call action, the functionality of mapping and filter

Suppose we have a PEOPLEARR array with the above. We now have to find the most recent landing user and remove the email address. In general, we usually use the following three steps:

  1. Filter out all objects without email;

  2. Extract the nearest object;

  3. Find the maximum value.

Let's get together, we can get the following code:

function notEmptyEmail(x) {
   return (x.email !== null) && (x.email !== undefined);
}

function getLastSeen(x) {
    return x.lastSeen;
}

function greater(a, b) {
    return (a > b) ? a : b;
}

const peopleWithEmail = peopleArr.filter(notEmptyEmail);
const lastSeenDates   = peopleWithEmail.map(getLastSeen);
const mostRecent      = lastSeenDates.reduce(greater, '');

console.log(mostRecent);
//>> 2019-05-13T11:07:22+00:00

The above code is taking into account both functions and good readability, while simple data can run well. But if we have a huge array, we may hit the memory problem. This is because we use variables to store each intermediate array. If we do some changes to the Reducer Callback method, we can work more than three steps at one time.

function notEmptyEmail(x) {
   return (x.email !== null) && (x.email !== undefined);
}

function greater(a, b) {
    return (a > b) ? a : b;
}
function notEmptyMostRecent(currentRecent, person) {
    return (notEmptyEmail(person))
        ? greater(currentRecent, person.lastSeen)
        : currentRecent;
}

let result = peopleArr.reduce(notEmptyMostRecent, '');

console.log(result);
//>> 2019-05-13T11:07:22+00:00

The above use of the use of reduuce () is only traversed by arrays, which greatly enhances performance. However, in the case where the amount of data is small, the performance advantage of this method is not protruding.

Another operation we can do is to use reduce(), which can run promises in series in a queue (as opposed to running promises in parallel). When you need to request a series of speed-limited APIs, and hope that each request will come in succession, and the next request will be sent after the previous request is completed, the following method is very useful. To give an example, let's suppose that we want to retrieve the message of each person in the peopleArr array from the server. We can do this:

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 the body of this function, p is a promise object, waiting for it to finish executing,

// Then run fetchMessages().
const obj = await p;
const data = await fetchMessages(username);
return {...obj, [username]: data};
}
const msgObj = peopleArr
.map(getUsername)
.reduce(chainedFetchMessages, Promise.resolve({}))
.then(console.log);
//>> {glestrade: [… ], mholmes: [… ], iadler: [… ]}

Pay attention to the logic of this code, we must call the promise callback function through promise. resolve() as the initial value of the reducer. It will immediately call the resolve method so that a series of API requests will start running one after another.