本博客学习书籍:《学习JavaScript数据结构与算法》
本博客学习章节:第六章,第七章
集合
集合是由一组无序且不能重复的项组成的。
为了更好的贴合这个定义,我们用JavaScript的对象来实现集合。JavaScript的对象不允许一个键指向两个不同的属性,保证了集合里的元素都是唯一的。
首先用函数法来模拟实现Set类。
function Set(obj) {
let items = obj || {};
// 集合里的元素是不能重复的,所以在add、remove的时候需要判断元素是否已存在
this.has = function(value){
return value in items;
// 或
// return items.hasOwnProperty(value);
}
// 添加
this.add = function (value) {
if(!this.has(value)){
items[value] = value;
return true;
}else{
return console.error('不能加入重复的项')
}
}
// 删除
this.remove = function (value) {
if(this.has(value)){
delete items[value];
return true;
}else{
return console.error('该项不存在')
}
}
// 清空
this.clear = function(){ items = {}; };
// 长度
this.size = function(){ return Object.keys(items).length; }
// values
this.values = function () {
// let values = [];
// for (let i = 0, keys = Object.keys(items); i < keys.length; i++) {
// values.push(items[keys[i]]);
// }
// return values;
return Object.values(items)
};
return items;
}
集合操作
我们对集合可以进行如下操作:
- 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。
- 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。
- 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。
- 子集:验证一个给定集合是不是另一集合的子集。
并集
并集就是把两个Set()
合并成一个新Set()
。我们Set类
的元素是用对象元素实现的,所以,新Set()
也是用对象元素实现的。
function Set(obj) {
let items = obj || {};
// Set类的基本实现方法 ...
// 并集
this.union = function (otherSet) {
let unionSet = new Set();
let values = this.values();
for (let i = 0; i < values.length; i++) {
unionSet.add(values[i]);
}
values = otherSet.values();
for (let i = 0; i < values.length; i++) {
unionSet.add(values[i]);
}
return unionSet;
};
return items;
}
交集
交集,查找第一个Set()
里有没有第二个Set()
里相同的元素。如果有的话,就push
到交集set()
里。
function Set(obj) {
let items = obj || {};
// Set类的基本实现方法 ...
// 交集
this.intersection = function(otherSet){
let intersectionSet = new Set();
let values = this.values();
values.forEach((v)=>{
if(otherSet.has(v)){
intersectionSet.add(v);
}
});
return intersectionSet;
}
return items;
}
差集
差集,查找第一个Set()
里有的而第二个Set()
里没有的元素,过滤出来就push
到差集set()
里。
function Set(obj) {
let items = obj || {};
// Set类的基本实现方法 ...
// 差集
this.difference = function(otherSet){
let differenceSet = new Set();
let values = this.values();
values.forEach((v)=>{
if(!otherSet.has(v)){
differenceSet.add(v);
}
});
return differenceSet;
}
return items;
}
子集
Set1()
的所有元素,都能在Set2()
找到,并且set1.length <= set2.length
那么我们就说Set1()
是Set2()
的子集。
function Set(obj) {
let items = obj || {};
// Set类的基本实现方法 ...
// 差集
this.difference = function(otherSet){
let differenceSet = new Set();
let values = this.values();
values.forEach((v)=>{
if(!otherSet.has(v)){
differenceSet.add(v);
}
});
return differenceSet;
}
return items;
}
ES6的Set类操作
我们可以在Set类的原型链上
添加并集等集合操作。
// 并集
Set.prototype.union = function (setA,setB) {
let unionAB = new Set();
for (let x of setA) unionAB.add(x);
for (let x of setB) unionAB.add(x);
return unionAB
};
// 交集
Set.prototype.intersection = function (setA,setB) {
let intersectionSet = new Set();
for (let x of setA){
if(setB.has(x)){
intersectionSet.add(x);
}
}
return intersectionSet
};
// 差集
Set.prototype.difference = function (setA,setB) {
let differenceSet = new Set();
for (let x of setA){
if(!setB.has(x)){
differenceSet.add(x);
}
}
return differenceSet
};
// 子集
Set.prototype.subset = function (setA, setB) {
if (setA.size > setB.size) {
return false;
} else {
for (let x of setA) {
if (!setB.has(x)) {
return false;
}
}
return true;
}
};
let set1 = new Set();
set1.add(11);
set1.add(22);
set1.add(33);
// Set(3) {11, 22, 33}
let set2 = new Set();
set2.add(22);
set2.add(33);
set2.add(44);
// Set(3) {22, 33, 44}
let set3 = new Set();
set3 = set3.union(set1,set2);
// Set(4) {11, 22, 33, 44}
let set4 = new Set();
set4 = set4.intersection(set1,set2);
// Set(2) {22, 33}
let set5 = new Set();
set5 = set5.difference(set1,set2);
// Set(1) {11}
let set6 = new Set();
set6.add(11);
set6.add(22);
// Set(2) {11, 22}
let set7 = new Set();
set7 = set7.subset(set6,set1);
// true
字典
在字典中,存储的是[键,值]对,其中键名是用来查询特定元素的。字典和集合很相似,集合以 [值,值]的形式存储元素,字典则是以[键,值]的形式来存储元素。
字典操作
我们用JavaScript的对象来实现字典,字典操作与Set类
很相似,但不同于存储[值,值]对的形式,我们将要存储的是 [键,值]对。
function Dictionary(obj) {
let items = obj || {};
// 字典里的元素是不能重复的,所以在add、remove的时候需要判断元素是否已存在
this.has = function(key){
// return key in items;
return items.hasOwnProperty(key);
};
// 添加
this.set = function (key, value) {
// 添加一个新的值,或更新一个已有的值
items[key] = value;
return items;
};
// 删除
this.delete = function (key) {
if(this.has(key)){
delete items[key];
return true;
}else{
return false;
}
};
// 获取某个元素
this.get = function (key) {
return this.has(key) ? items[key] : undefined;
};
// values
this.values = function () {
let values = [];
for(let k in items){
if(this.has(k)){
values.push(items[k]);
}
}
return values;
};
// keys
this.keys = function () {
// 要取出一个对象obj中所有的键名,可以用Object.keys(obj)
return Object.keys(items);
};
// getItems
this.getItems = function () {
return items;
};
// 清空
this.clear = function(){ items = {}; };
// 长度
this.size = function(){ return Object.keys(items).length; }
}
let dict1 = new Dictionary({'a':'111qq.com','b':'22qq.com','c':'333qq.com'});
dict1.has('c'); // true
dict1.getItems(); // {a: "111qq.com", b: "22qq.com", c: "333qq.com"}
dict1.size(); // 3
dict1.keys(); // (3) ["a", "b", "c"]
dict1.values(); // (3) ["111qq.com", "22qq.com", "333qq.com"]
dict1.get('a'); // "111qq.com"
dict1.set('d','555qq.com') // {a: "111qq.com", b: "22qq.com", c: "333qq.com", d: "555qq.com"}
dict1.delete('d') // true
dict1.getItems() // {a: "111qq.com", b: "22qq.com", c: "333qq.com"}
ES6的Map类
上面的Dictionary类完全可以用ES6的Map类来代替。
let map1 = new Map();
map1.set('a','111qq.com');
map1.set('b','22qq.com');
map1.set('c','333qq.com');
// Map(3) {"a" => "111qq.com", "b" => "22qq.com", "c" => "333qq.com"}
map1.has('c'); // true
map1.size; // 3
map1.keys(); // MapIterator {"a", "b", "c"}
map1.values(); // MapIterator {"111qq.com", "22qq.com", "333qq.com"}
map1.get('a'); // "111qq.com"
其实,es6的Set类
,Map类
,就是封装好的更语义化更方便的对象,这种对象是用function构造函数
实现的。
typeof Set
// "function"
typeof Map
// "function"
typeof WeakMap
// "function"
typeof WeakSet
// "function"
ES6的WeakMap类和WeakSet类
除了Set和Map这两种新的数据结构,ES6还增加了它们的弱化版本,WeakSet和 WeakMap。
基本上,Map和Set与其弱化版本之间仅有的区别是: WeakSet或WeakMap类没有entries、keys和values等方法; 只能用对象作为键。 创建和使用这两个类主要是为了性能。WeakSet和WeakMap是弱化的(用对象作为键),没有强引用的键。这使得JavaScript的垃圾回收器可以从中清除整个入口。
另一个优点是,必须用键才可以取出值。这些类没有entries、keys和values 等迭代器方法,因此,除非你知道键,否则没有办法取出值。
- WeakMap类也可以用set方法,但不能使用数字、字符串、布尔值等基本数据类 型,需要将名字转换为对象;
- 搜索、读取和删除值,也要传入作为键的对象。
同样的逻辑也适用于WeakSet类。
let map3 = new WeakMap();
let obj1 = {name:'sun'}, obj2 = {name:'leaf'}, obj3 = {name:'rivers'};
map3.set(obj1,'summer');
map3.set(obj2,'spring');
map3.set(obj3,'autumn');
map3.has(obj1) // true
map3.get(obj2) // "spring"
// WeakMap {{…} => "autumn", {…} => "summer", {…} => "spring"}