Data structure - array

Posted by saandel on Fri, 17 Sep 2021 01:27:38 +0200

Arrays are everywhere. When you need more than one data transmission or display, you will use arrays.

Now let's start with the definition.

array define

An array is a group of data, as shown in:

[1,3,5,7]
Copy code

In JavaScript, array elements can be of any type, but generally, a set of data of the same type is more common and meaningful.

You can define arrays in the following ways:

  • Literal
  • Array() constructor

Literal

The most simple and intuitive, but also one of the most commonly used.

let arr = [1,2,3]
Copy code

Here are two phenomena that need attention:

let arr = [1,,3]  //Accessing arr[1] will be undefined
let arr = [,,] //The array length will be 2
 Copy code

Array() constructor

Constructor is one of the most reasonable ways to create arrays. Like this.

let arr1 = new Array(1,2,3,4)   //[1, 2, 3, 4]
let arr2 = new Array(1)  // [ <1 empty item> ]
let arr3 = new Array('1')  // [ '1' ]
Copy code

be careful:

  • When the parameters of the constructor are multiple, each item is created as an element.
  • When there is only one number, it indicates the length of the array, the elements are not filled, and the access will also be undefined.
  • If there is only one non number, an array of single elements is created.

Common array methods and Applications

Array has many methods and wide uses. They are the key to the power of array.

If you are a beginner, don't expect to be able to remember by reading. In those years, the author read the array chapter of elevation 3 several times and didn't remember it. However, it doesn't mean that there is no way to help memory. If you combine it with practical application scenarios, you can better remember it.

Let's count them one by one.

Type judgment - Array.isArray()

Sometimes, we need to judge whether the obtained data is an array, otherwise there may be an error in method call. The following methods are usually used:

  • instanceof

Judge whether the left side of the operator is an instance of the right type, that is, a instanceof b. At this point, we can set b as Array and return the Boolean value.

  • constructor

When we talked about creating objects earlier, we mentioned the constructor. Reference types have corresponding constructors. The constructor of Array instances is Array. Therefore, you can check whether a is an Array instance by using a.constructor == Array is true.

  • Object.prototype.toString.call()

This method is designed to convert the instance type into a string and then output it. If the instance is an array type, it will output '[object Array]' and then make a judgment. Because the first two methods may have some uncertain problems, this method was once considered the most accurate and reliable method, and the same is true for judging other reference types.

The emergence of ES6 provides a new judgment method - isArray(). Only Array.isArray(obj) is needed to judge whether obj is an array, which provides great convenience.

Add and delete - push()/pop(), unshift()/shift()

After the array is created, there may or may not be elements. These two groups of methods are often used to dynamically add or delete elements to the array, but their methods are limited and can only be operated from both ends of the array, but they are enough for suitable scenarios.

What is a suitable scenario? Only the elements that meet the conditions, without paying attention to the order and no other additional conditions, can be handled simply and roughly.

const arr = [],ele;
if(ele > 1){
    arr.push(ele)  //The corresponding deletion is pop()
}
Copy code

The other pair is the same.

Add or delete anywhere -- splice()/slice()

Since the above methods have limitations, this group is more flexible. Its flexibility is reflected in that it is no longer limited to locations. It can be added, deleted and replaced at any location. Which one depends on the parameters passed.

Parameter format: splice (index, num, Item1,..., itemx)

They represent:

index - start position

Num -- number of vacant positions

Item1,..., itemx -- what elements are added from the empty position

The first two parameters are required and the third one is optional. It can be concluded that:

As long as a legal value is assigned to index, the operation position can be selected. If the second bit is 0, the element will not be deleted. At this time, if the third parameter has a value, the element will be added to the specified position.

If the second parameter is a positive integer other than 0, the specified number of elements will be deleted. At this time, if the third parameter has data, it will be filled in the position where the element is deleted to replace the element.

So what is slice() and how is it different?

Slice looks very similar to slice, only one letter short, but its purpose is very different. There are two main differences:

1, slice receives two parameters, begin and end, which determine which parts of the source array to intercept. The intercepted parts include begin but not end

2, slice returns a new array. This new array is a shallow copy of the source array, and the source array is not affected

Therefore, the use of these two methods can be summarized as follows: if you want to process based on the source array, intercept a part without changing the source array, use slice, and use splice in other cases.

Index of a specific element -- indexOf()/findIndex()

In the previous method, we mentioned "element" and "location". Many times, we don't know the location of an element. We need to obtain it dynamically. At this time, it is useful to find the index.

The results obtained by the two methods are similar, but there are differences in usage.

  • indexOf(searchElement[, fromIndex])

The indexOf() method needs to pass in the specific search element and the starting index (optional).

const nums = [1, 2, 3, 4, 5];

nums.indexOf(3); //2
 Copy code
  • The findIndex() method passes in a callback function

The function supports three optional parameters: element, index and array itself. Usually, using the first two or even one parameter is enough. Like this:

const nums = [1, 2, 3, 4, 5];
let targetIndex = nums.findIndex(target => target  == 3) //2
 Copy code

It should be noted that sometimes the result may be different from the expectation, that is, when there are multiple identical target elements in the array, they will only return the position of the first target element.

const nums = [1, 2, 3, 3, 5];
nums.indexOf(3); //2
let targetIndex = nums.findIndex(target => target  == 3) //2
 Copy code

This is normal. If there is an exception, such as the element does not exist, both will return - 1.

Find element -- includesOf()/find()

The previous group of methods is to find the position of an element in the array. Of course, by the way, you can judge whether the element exists by whether the return value is - 1. This group of methods is to directly obtain whether the element exists in the array.

  • includesOf() -- returns a Boolean value
const nums = [1, 2, 3, 3, 5];
nums.includes(1) //true
nums.includes(8) //false
 Copy code
  • find() - returns the target value itself
const nums = [1, 2, 3, 3, 5];
nums.includes(1) //1
nums.includes(8) //undefined
 Copy code

Fill - fill()

When creating an array, you can create an empty array and add it. You can also use literal quantity to create an existing array. You can also use splice to add, delete and change the array, but there is another way to change the array - fill().

Look at the usage.

const arr = new Array()
arr.fill(1) //[]
Copy code

Oh, it seems that the car overturned. What about the good filling? Why is it still an empty array?

Wait a minute, the fill() method is not used like this. The premise of using it is that there are a certain number of elements in the array. For example:

You can do this:

const arr = new Array(1,2,3,4)
arr.fill(5) // [ 5, 5, 5, 5 ]
Copy code

You can do the same

const arr = new Array(4)
arr.fill(5) // [ 5, 5, 5, 5 ]
Copy code

Thus, a method for quickly establishing a certain number of non empty arrays can be obtained.

Now let's look at the complete syntax: arr.fill(value[, start[, end]])

Deja vu. It also receives two position parameters, a start position and an end position. When we don't transmit them above, they default from beginning to end. We can set them and try.

const arr = [1,2,3,4,5,6]
arr.fill(5,2,4) // [ 1, 2, 5, 5, 5, 6 ]
Copy code

However, the fill() method is prone to make a mistake. When the filled element is a reference type, the filled value will be the same reference, for example, initializing a commodity list.

const goods = {
    name:'Jewellery',
    price:10,
    weight:'20'
}

const goodsList = new Array(8)
goodsList.fill(goods)
Copy code

The product list data will be as follows:

[
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' },
  { name: 'Jewellery', price: 10, weight: '20' }
]
Copy code

Then we edit the first item and change the price to 8

goodsList[0].price = 8
 Copy code

But found that the price of each commodity has been changed.

[
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' },
  { name: 'Jewellery', price: 8, weight: '20' }
]
Copy code

This is obviously not the desired effect.

Not only that, don't forget that arrays are also reference types. Therefore, this problem will also occur when initializing two-dimensional arrays. Therefore, if there is such a requirement that a set of data items to be edited / modified need to be prepared during page initialization, this method is not suitable for creation.

Sort - sort()

Sorting is a common requirement. When a list is involved, there must be sorting by time, price, sales volume, etc.

The simplest is to sort a set of numbers.

const numSort = [3,2,8,6,5,7,9,1,4].sort()
numSort //[1, 2, 3, 4, 5, 6, 7, 8, 9]
Copy code

So simple, what can I say?

Of course, a small example successfully deceived us. Is it arranged from small to large according to the size of the number? No, don't believe it.

Let's change the above array:

const numSort = [3,2,8,10,6,20,5,7,9,1,4].sort()
//[1, 10, 2, 20, 3, 4, 5, 6, 7, 8, 9]
Copy code

Something magical happened, 10 to 2? 20 less than 3?

Note that the sort() method actually receives a function, which specifies the order according to the return value of the function. If the function is omitted, it will be sorted according to the Unicode sites of the characters that convert the elements into strings. So, if you want to sort by the real size of the numbers here, you can write this.

[3,2,8,10,6,20,5,7,9,1,4].sort((a,b) => a-b)
Copy code

Based on what? Is the algorithm rule of sorting function:

  • If a - b is less than 0, a will be arranged before b;
  • If a - b is equal to 0, the relative positions of a and b remain unchanged;
  • If a - b is greater than 0, b will be arranged before a.

If the comparison object is a string, the method is the same. Therefore, generally, don't be lazy. We can make full use of this feature to customize the required rules.

The above discussion is about sorting numbers or strings. In daily needs, it is often not so simple. A column of object arrays containing multiple attributes may be sorted, such as price, sales volume, etc.

How to sort an array according to an attribute.

In fact, it's not difficult. Similarly, take the goods list above as an example. If you sort by price, you only need to do the following:

goodList.sort((a,b)=>{
    return (a.price - b.price)
})
Copy code

That's it.

When it comes to sorting, by the way, reverse is also a sort, but it has no rules. Directly inverting a group of elements head and tail, [1,2,3] will become [3,2,1], ['a','f','c '] will become ['c', 'f', 'a'].

Merge -- concat() / extend operation

Ideally, it is best for us to obtain an array and operate on an array, but sometimes there are more than one data source, possibly two or more. When displaying or transmitting, we need to combine them into one, so we need to use the merge method. The traditional method is concat().

const primary = [1,2,3,4,5,6], 
      middle = [7,8,9], 
      high = [10,11,12];
const grade = primary.concat(middle,high)  
//[1,  2, 3, 4,  5,  6,  7, 8, 9, 10, 11, 12]
Copy code

But if you think that's all, you're wrong again.

  • concat() can be used to combine not only arrays, but also an array and other types of values, such as numbers, strings, etc.
primary.concat(7,8) // [1,  2, 3, 4,  5,  6, 7, 8]
Copy code
  • When concat() merges arrays, it does not change the original array, but returns a new array. However, the new array contains a shallow copy of the original array.
const primary = [{a:1}], 
      middle = [{b:2}];
const grade = primary.concat(middle)
//[ { a: 1 }, { b: 2 } ]
primary[0].a = 2
middle[0].b = 3
// [ { a: 2 }, { b: 3 } ]
Copy code

Reference types always bring us "surprises", so we should pay more attention to them when using them.

Of course, the extension operator is still concise. The above operations only need to:

const grade = [...primary,...middle,...high]
Copy code

Return a new array -- map()/filter()

What does the new array mean?

In most cases, the data we get is an array composed of objects. Objects are collections that contain many things, including their own data and other data associated with them, ranging from a few to dozens. However, we don't need to carry them all when transmitting or displaying them, or we need to process them on the original basis, At this time, you can return the processed new array as needed.

For example:

[
  { name: 'Jewellery', price: 1000, weight: '20' },
  { name: 'mobile phone', price: 2000, weight: '20' },
  .
  .
  .
  { name: 'computer', price: 5000, weight: '20' }
]
Copy code

We get a list of products, but we just need to take out the name and use it.

let nameList = goodsList.map(item=>{
    return item.name
})
['Jewellery'.'mobile phone',...,'computer']
Copy code

Or, we need to discount all goods on the basis of the original price, so as to:

let priceDiscountList = goodsList.map(item=>{
    item.price *= 0.5 
    return item
})
[
  { name: 'Jewellery', price: 500, weight: '20' },
  { name: 'mobile phone', price: 1000, weight: '20' },
  .
  .
  .
  { name: 'computer', price: 2500, weight: '20' }
]
Copy code

Of course, this link will not be done in the actual project, otherwise the JS logic code will be changed every time there is a change, which is unfavorable in terms of ease of use, efficiency and maintenance, just to illustrate the usage.

After introducing the map, look at the filter. The literal meaning is well understood. Filtering will be filtered only if it meets the conditions. It also receives a function.

For example, we will find out those with a price of more than 500.

let priceLowList = goodsList.filter(item=>{
    return item.price > 500
})
[
  { name: 'mobile phone', price: 1000, weight: '20' },
  .
  .
  .
  { name: 'computer', price: 2500, weight: '20' }
]
Copy code

These two methods are very common in actual projects. The only thing to note is their working mode. They both generate new arrays. map needs to return array elements, and fliter is the filter condition. Remember "return"!

Iterative processing - forEach()

This method is very similar to the above two methods. Basically, each element of the array can be accessed and processed accordingly. The difference is that this method is only used for iteration, just like the previously used for loop. Of course, the simplicity of function means that there is more operational space.

For example, we can achieve an effect similar to map.

let nameList = []
goodsList.forEach(item=>{
    nameList.push(item.name)
})
Copy code

You can also achieve an effect similar to filter.

let priceLowList = []
goodsList.forEach(item=>{
    if(item.price > 500){
        priceLowList.push(item)
    }
})
Copy code

Yes, you can write any logic you want, and it is more efficient than the for loop and more in line with the functional programming paradigm.

Element judgment -- some()/every()

It is also used for checking, receiving callback functions and writing judgment logic. The difference is that some() is true if "there is conformity", while every() is true if "all conformity", similar to | and & &. It is relatively simple and will not be repeated.

duplicate removal

When the user's operations are large and uncertain, it is inevitable to repeat. Sometimes we only need to know whether a value exists, rather than multiple duplicate values. At this time, we need to de duplicate the array (of course, there are other methods to ensure a single value, and the focus here is de duplication).

There are many de duplication methods, and the principle is similar - by traversing the array for comparison, we can ensure that the value is unique. Here are three kinds of reference:

  • includes
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
        if( !array.includes( arr[i]) ) {   //Detects whether the array has a value
            array.push(arr[i]);
        }
    }
    return array
}
Copy code
  • filter
function unique(arr) {
    return arr.filter(function(item, index, arr) {
      //Current element, the first index in the original array = = the current index value, otherwise the current element is returned
      return arr.indexOf(item, 0) === index;
    });
  }
Copy code
  • Set
function unique (arr) {
  return Array.from(new Set(arr))
}


 

Topics: Javascript data structure array