在 ES 6 之前,我们要保存多个数据只能用数组或集合,这是很匮乏的,可选性太少了,于是在 ES 6 提出了 set 和 map 两个新的数据类型,这两个数据类型都可以用于保存多个数据。
set 集合可以用于保存多个数据,类似于数组,但是 set 集合有一个很大的特点,就是天然去重,也就是 set 集合中的数据是不可能重复的。
接下来使用代码来演示 set 集合的使用
js
new Set();//创建一个空的Set集合
const s = new Set();//这就是一个空的Set集合
const s1 = new Set([1,2,3,4]);//在创建Set的时候,可以传入一个可迭代对象,这里传入一个数组,此时Set集合就会根据可迭代对象的迭代结果来初始化内容。这里实际上就实现了可迭代对象向Set集合的转换。
const s2 = new Set([1,2,3,4,4]);//如果数组中有重复数据,会自动去重
s1.size;//只读属性,这个属性可以用于拿到Set集合的尺寸,相当于数组的length
s1.add(2);//添加一个数据在set集合的末位,注意,set集合是有序的,如果数据已存在,不进行任何操作
//注意,set集合使用Object.is()函数来判断两个数据是否相同,但是针对+0和-0,set认为是同一个数据。
s1.has(3);//可以判断Set中是否存在指定数据,判断方式同上,返回一个布尔值
s1.delete(3);//删除Set集合中指定的数据,注意,这里是按值删除,返回一个布尔值,表示是否删除成功。
s1.clear();//清空整个Set集合
const arr = [...s];//由于Set本身是一个可迭代对象,迭代的每一个结果为每一项的数据,所以我们可以使用展开运算符来将Set集合转换为数组。
//这里结合前面的数组转Set,可以很轻松的实现数组去重
[...new Set([1,2,3,4,5,1,2,4,6])];//这里就可以得到一个去重后的数组。
//由于Set本身是一个可迭代对象,所以我们可以使用for...of循环遍历
for(const item of s){
console.log(item);
}
//使用Set中的forEach方法遍历
s.forEach(item => {//在Set集合中,不存在下标,所以在forEach的回调函数中,我们也获取不到他的下标,这里的forEach的回调函数也可以传入三个参数,第一个参数和第二个参数一模一样,都表示值,第三个参数为Set集合本身。
console.log(item);
})
//Set集合很常见是做法是求两个数组的交集,并集,差集
const arr1 = [1,2,3,4];
const arr2 = [3,4,5,6];
function union(arr1,arr2){//并集
const arr = arr1.concat(arr2);
return [...new Set(arr)];
}
function intersetction(arr1,arr2){//交集
const newArr1 = [...new Set(arr1)];
arr2 = new Set(arr2);
return newArr1.filter(item =>{
return arr2.has(item);
});
}
function difference(arr1,arr2){//arr1 - arr2
arr1 = [...new Set(arr1)];
arr2 = new Set(arr2);
return arr1.filter(item => {
return !arr2.has(item);
});
}接下来手写 Set 集合
js
/**
* 类 MySet 实现一个简单的集合数据结构。
*/
class MySet {
/**
* 构造函数,初始化集合。
* @param {Iterable} obj 可迭代对象,用于初始化集合。
*/
constructor(obj) {
this.size = 0;
// 如果传入对象可迭代,则遍历并添加到集合中
if (obj[Symbol.iterator]) {
for (const item of obj) {
this.add(item);
}
}
}
/**
* 实现集合的迭代器方法,使其可遍历。
* @yields {*} 遍历集合中的每个元素。
*/
*[Symbol.iterator]() {
for (let i = 0; i < this.size; i++) {
yield this[i];
}
}
/**
* 检查集合中是否存在指定元素。
* @param {*} item 要检查的元素。
* @returns {boolean} 元素存在返回 true,否则返回 false。
*/
has(item) {
// 遍历集合,进行存在性检查
for (const n of this) {
if (item === n || (Number.isNaN(n) && Number.isNaN(item)))
return true
}
return false;
}
/**
* 从集合中删除指定元素。
* @param {*} item 要删除的元素。
* @returns 无返回值。
*/
delete(item) {
// 先检查元素是否存在
if (!this.has(item)) {
return;
}
// 标记找到要删除的元素,然后将其从集合中移除
let index = 0;
let flag = false;
for (const n of this) {
if (flag) {
this[index - 1] = this[index];
} else {
if (item === n || (Number.isNaN(n) && Number.isNaN(item))) {
flag = true;
}
}
index++;
}
this.size--;
delete this[this.size];
}
/**
* 向集合中添加一个元素。
* @param {*} item 要添加的元素。
* @returns 无返回值。
*/
add(item) {
// 如果元素不存在于集合中,则添加
if (!this.has(item)) {
this[this.size] = item;
this.size++;
}
}
/**
* 清空集合,移除所有元素。
* @returns 无返回值。
*/
clear() {
// 遍历集合,逐个删除元素
for (let i = 0; i < this.size; i++) {
delete this[i];
}
this.size = 0;
}
/**
* 对集合中每个元素执行给定的回调函数。
* @param {Function} fn 回调函数,接受三个参数:当前元素,当前元素,集合本身。
* @returns 无返回值。
*/
forEach(fn) {
// 遍历集合,执行回调函数
for (const item of this) {
fn(item, item, this);
}
}
}