ES6的Set和Map数据结构
Set
基本用法
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
Set
函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
const set = new Set([1, 2, 3, 4, 4]);
[...set] // [1, 2, 3, 4]
Set 可以用来数组去重:
[...new Set(array)]
还能去除字符串里面的重复字符:
[...new Set('ababbc')].join('')
// "abc"
Set 实例的属性和方法
Set 结构的实例有以下属性。
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
Set.prototype.add(value)
:添加某个值,返回 Set 结构本身。Set.prototype.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。Set.prototype.clear()
:清除所有成员,没有返回值。
const s = new Set();
s.add(1).add(2).add(2);
console.log(s.size); // 2
console.log(s.has(1)); // true
console.log(s.has(2)); // true
console.log(s.has(3)); // false
s.delete(2);
console.log(s.has(2)); // false
Array.from
方法可以将 Set 结构转为数组,这是数组去重的第二种方法。
Array.from(new Set([1, 2, 3, 4, 4])); // [1, 2, 3, 4]
遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys()
:返回键名的遍历器Set.prototype.values()
:返回键值的遍历器Set.prototype.entries()
:返回键值对的遍历器Set.prototype.forEach()
:使用回调函数遍历每个成员
需要特别指出的是,Set
的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。
keys()、values()、entries()
keys
方法、values
方法、entries
方法返回的都是遍历器对象,keys
方法和values
方法返回值是相同的。
Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values
方法,所以遍历 Set 的 value
方法和直接遍历 Set 的结果是相同的。
let set = new Set(['red', 'green', 'blue']);
// 这三种遍历方式的结果都是相同的
for(let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for(let item of set.values()) {
console.log(item);
}
for(let item of set) { // 在遍历时,可以省略value方法
console.log(item);
}
for(let item of set.entries()) {
console.log(item);
}
// ['red', 'red']
// ['green', 'green']
// ['blue', 'blue']
forEach()
和数组的forEach()
方法类似,可以传入一个回调函数,接收三个参数(键值、键名、集合本身),第一个参数和第二个参数是相同的。
let set = new Set(['red', 'green', 'blue']);
set.forEach((value, key) => {
console.log(value + ':' + key);
});
// red:red
// green:green
// blue:blue
遍历的应用
利用扩展运算符可以间接地使用数组的map
和filter
方法
let set = new Set([2, 3, 4]);
set = [...set].map(item => item * 2); // // 返回Set结构:{2, 4, 6}
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]); // Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x))); // set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x))); // Set {1}
Map
含义和基本用法
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构)。Map 数据结构类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应。
Map 实例的属性和方法
- size 属性:返回 Map 结构的成员总数
Map.prototype.set(key, value)
:设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。Map.prototype.get(key)
:读取key
对应的键值,如果找不到key
,返回undefined
。Map.prototype.has(key)
:返回一个布尔值,表示某个键是否在当前 Map 对象之中。Map.prototype.delete(key)
:删除某个键,返回true
。如果删除失败,返回false
。Map.prototype.clear()
:清除所有成员,没有返回值。
const map = new Map();
map.set('name', 'Tom');
map.set('age', 20);
console.log(map.size); // 2
console.log(map.get('name')); // Tom
console.log(map.has('age')); // true
console.log(map.has('sex')); // false
map.delete('age');
console.log(map.has('age')); // false
map.clear();
console.log(map.size); // 0
遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。
需要特别注意的是,Map 的遍历顺序就是插入顺序。
Map 结构的默认遍历器接口(Symbol.iterator
属性),就是entries
方法,所以遍历 Map 的 entries
方法和直接遍历 Map 的结果是相同的。
const map = new Map([
['foo', true],
['bar', false]
]);
for(let key of map.keys()) {
console.log(key);
}
// foo
// bar
for(let value of map.values()) {
console.log(value);
}
// true
// false
for(let item of map.entries()) {
console.log(`${item[0]} : ${item[1]}`);
}
// foo : true
// bar : false
for(let [key, value] of map.entries()) {
console.log(`${key} : ${value}`);
}
// foo : true
// bar : false
for(let [key, value] of map) { // 在遍历所有成员时,可以省略entries方法
console.log(`${key} : ${value}`);
}
// foo : true
// bar : false
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...
)。
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
结合数组的map
方法、filter
方法,可以实现 Map 的遍历和过滤(Map 本身没有map
和filter
方法)。
const map0 = new Map([
[1, 'a'],
[2, 'b'],
[3, 'c'],
]);
const map1 = new Map([...map0].filter(([k, v]) => k === 2));
// {2 => 'b'}
const map2 = new Map([...map0].map(([k, v]) => [k * 2, '_' + v]));
// {2 => '_a', 4 => '_b', 6 => '_c'}
此外,Map 还有一个forEach
方法,与数组的forEach
方法类似,也可以实现遍历。
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
map.forEach((key, value) => {
console.log(`${key} : ${value}`);
});
// one : 1
// two : 2
// three : 3
参考资料: