在项目实践中用更优雅的方式处理数组问题

26

图片描述

在最近的项目中,遇到了比较多处理数组的场景,比如要对数组里面某个元素的某一个字段进行抽取归类,或者判断数组当中的某个元素是否符满足判断条件等。

网上关于使用ES5新的的API来代替for循环的文章已经非常多,它们有的详细讨论了API的用法,有的详细分析各自的性能,还有的整理了使用中的注意事项。因此,本文不再对这些API的详细使用方式进行赘述,仅仅从个人角度出发,整理归纳一些在项目实践中遇到的能够更加优雅处理数组遍历的例子。

1、使用Set处理数组去重和元素剔除问题

Set是es6新增的一种数据结构,它和数组非常相似,但是成员的值都是唯一的,没有重复的值。它提供了4个语义化的API:

  1. add(value):添加某个值,返回Set结构本身。

  2. delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

  3. has(value):返回一个布尔值,表示该值是否为Set的成员。

  4. clear():清除所有成员,没有返回值。

参考自@阮一峰 老师的《ECMAScript 6 入门》

那么我们可以用Set来干嘛呢?

第一个用法,数组去重。对于一个一维数组,我们可以先把它转化成Set,再配合...解构运算符重新转化为数组,达到去重的目的。请看例子:

const arr = [1, 1, 2, 2, 3, 4, 5, 5]

const newArr = [...new Set(arr)]

console.log(newArr)

// [1, 2, 3, 4, 5]

值得注意的是,这个方法不能对元素为“对象”的数组奏效:

const arr = [{ name: 'Alice', age: 12 }, { name: 'Alice', age: 12 }, { name: 'Bob', age: 13 }]

const newArr = [...new Set(arr)]

console.log(newArr)

// [{ name: 'Alice', age: 12 }, { name: 'Alice', age: 12 }, { name: 'Bob', age: 13 }]

这是因为Set判断元素是否重复的办法类似于===运算符,两个对象总是不相等的。

除了去重,Set提供的delete()方法也是非常实用。在以往的做法中,如果要删除数组中指定的元素,我们需要先获取该元素所在下标,然后通过splice()方法去删除对应下标的元素,在理解上容易引起混乱:

// 我想删除数组当中值为2的元素
const arr = [1, 2, 3]
const index = arr.indexOf(2)
if (index !== -1) {
    arr.splice(index, 1)
}

console.log(arr)

// [1, 3]

使用Set就清晰多了:

const arr = [1, 2, 3]
const set = new Set(arr)
set.delete(2)
arr = [...set]

console.log(arr)

// [1, 3]

2、 使用map()方法和对象解构语法提取字段

请求后台接口返回的数据中,很可能会遇到下面这种数据格式:

studentInfo = [
  { name: 'Alice', age: 18, no: 2 },
  { name: 'Bob', age: 16, no: 5 },
  { name: 'Candy', age: 17, no: 3 },
  { name: 'Den', age: 18, no: 4 },
  { name: 'Eve', age: 16, no: 1 },
]

当我们要获取姓名列表、年龄列表和编号列表的时候,我们可以通过map()再配合对象的解构语法方便快捷地进行处理:

const nameList = studentInfo.map(({ name }) => name)
const ageList = studentInfo.map(({ age }) => age)
const noList = studentInfo.map(({ no }) => no)

// nameList: [ 'Alice', 'Bob', 'Candy', 'Den', 'Eve' ]
// ageList: [ 18, 16, 17, 18, 16 ]
// noList: [ 2, 5, 3, 4, 1 ]

3、使用filter()方法和对象解构语法过滤数组

接上上面的例子,如果我想获取一个“年龄小于等于17岁”的新列表,应该怎么做呢?类似map()方法,我们可以用filter()方法进行过滤:

const newStudentInfo = studentInfo.filter(({ age }) => {
  return age <= 17
})

/*
newStudentInfo: [
  { name: 'Bob', age: 16, no: 5 },
  { name: 'Candy', age: 17, no: 3 },
  { name: 'Eve', age: 16, no: 1 }
]
*/

4、借助includes()方法求两个数组的差集

假设我们有以下两个数组:

var a = [1, 2, {s:3}, {s:4}, {s:5}]
var b = [{s:2}, {s:3}, {s:4}, 'a']

我们应该如何找到它们的差集呢?传统的方法可能需要把它们以Object形式hash化,但其实我们可以通过.includes()方法更加优雅方便地找出差集,代码如下:

var a = [1, 2, {s:3}, {s:4}, {s:5}].map(item => JSON.stringify(item))
var b = [{s:2}, {s:3}, {s:4}, 'a'].map(item => JSON.stringify(item))

var diff = a.concat(b)
            .filter(v => !a.includes(v) || !b.includes(v))
            .map(item => JSON.parse(item))
            
// diff: [1, 2, {s:5}, {s:2}, "a"]

至于为什么要JSON.stringify(),是因为要对比两个“对象元素”是否相等,是无法直接以“对象”形式比较的(永远返回不相等)。

5、后记

本文篇幅较短,难度也相对简单,以上都是一些平时实践中发现的技巧,希望可以对读者们有所启发。如果你也有一些比较优雅好玩的技巧,不妨和我交流分享喔~


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

oldfu · 2017年06月28日

var arr = [1, 2, 3]
console.log(arr.remove(2));结果是【1,3】
如果再console.log(arr),j结果还是【1,2,3】

+4 回复

2

已经把例子改过来了,谢谢指正

jrainlau 作者 · 2017年06月28日
oldfu · 2017年06月28日

很实用

回复

0

thx~

jrainlau 作者 · 2017年06月28日
atzcl · 2017年06月29日

学习了

回复

aaawhz · 2017年12月07日

已收藏

回复

载入中...