Set和 Map 数据结构
18888628835 opened this issue · 0 comments
Set
Set
是ES6
新产生的数据解构,它类似于数组,不一样的是它没有重复的值。
生成Set数据解构
Set本身是构造函数,所以我们需要用new来生成一个Set数据解构
参数生成
const set=new Set([1,2,3,4,5])
set //Set(5) {1, 2, 3, 4, 5}
传递一个数组可以生成Set数据解构,实际上并非数组可以实现,具有iterable
接口的数据都可以通过这种方式生成Set数据。
原型方法生成生成
使用Set.prototype.add
方法也可以生成Set数据解构
const s= new Set()
for(let v of [1,2,3,1,2,3]){
s.add(v)
}
// Set(3) {1, 2, 3}
上面可以看到,Set并不会录重复值
iterable接口数组生成示例
function fn(){
return new Set(arguments)
}
fn(1,2,3,4,5,6)
// Set(6) {1, 2, 3, 4, 5, 6}
const set=new Set()
document.querySelectorAll('div').forEach(div=>{set.add(div)})
去重
const a=[1,2,1,2]
const b=[...new Set(a)]
上面代码使用...展开运算符来展开Set数据解构,再用[]
包起来就可以变成数组
或者也可以使用Array.from()
转数组,取决于你的爱好
const a=[1,2,1,2]
const b=Array.from(new Set(a))
字符串也可以去重
const a='123412'
const b=[...new Set(a)].join('')
b // '1,2,3,4'
上面的代码用于字符串去重,因为字符串也具有iterable
接口,可以当参数传递给Set
构造函数
Set实例的属性
Set.prototype.constructor
:默认为构造函数Set
Set.prototype.size
:返回Set实例的成员总数,跟数组的length差不多
Set实例的方法
操作方法
增:Set.prototype.add(value)
删:Set.prototype.delete(value)
查:Set.prototype.has(value)
清除:Set.prototype.clear()
遍历操作方法
Set.prototype.keys()
:返回键名的遍历器
Set.prototype.values()
:返回键值的遍历器
Set.prototype.entries()
:返回键值对的遍历器
Set.prototype.forEach()
:使用回调函数遍历每个成员
由于Set数据结构的键和值是一样的,所以keys
和values
方法返回的结果是一致的,我们可以通过entries论证键和值是一致的这个观点
let set= new Set(['qiu','yan','xi'])
set.forEach((value,key)=>{
console.log(`value:${value}=>key:${key}`)
})
//value:qiu=>key:qiu
//value:yan=>key:yan
//value:xi=>key:xi
for(let item of set.entries()){
console.log(item)
}
//['qiu','qiu']
//['yan','yan']
//['xi','xi']
上面代码中,entries每次都会返回包含键值的数组。
遍历的应用
由于Set拥有iterable
接口,也可以使用for of循环。
let set= new Set(['qiu','yan','xi'])
for(let x of set){
console.log(x)
}
//qiu
//yan
//xi
配合...运算符
就可以实现变相实现set
的map
或者filter
等方法
let set=new Set([1,2,3,4,5,6])
new Set([...set].map((value,key)=>{return value*2}))
//Set(6) {2, 4, 6, 8, 10, 12}
new Set([...set].filter((value)=>{
return value>3
}))
//Set(3) {4, 5, 6}
Map
javascript
中,object
对象是以键值对的形式存在的,但是其键全部都是字符串,当我们希望使用hash表的时候,ES6提供的Map比对象更适合,它打破了键值对中键为字符串的限制,提供以值值对的数据结构。
生成Map数据结构
参数生成
Map也是一个构造函数,我们可以采用传入一个二元数组的方式来生成数据结构,数组内的数组会形成值值对。
let map=new Map([['name','qiuyanxi'],['age',10]])
//Map(2) {"name" => "qiuyanxi", "age" => 10}
原型方法生成
Map键的位置也可以是对象,我们采用set方法来生成Map数据结构(添加成员)
let map=new Map().set({name:'qiuyanxi'},'男')
//Map(1) {{name:'qiuyanxi'} => "男"}
实际上,在Map构造函数接收数组为参数时,实际上做的是以下操作
let array=[[name,'qiu'],[age,10]]
let map = new Map()
array.forEach(([key,value],index)=>{map.set(key,value)})
//Map(2) {"name" => "qiu", "age" => 10}
上面的代码中[key,value]
实际上就是将[name,'qiu']
进行解构赋值,然后对map对象进行set方法添加对应的成员。
事实上,只要持有iterable
接口,且每个成员都是双元素数组结构都可以成为Map构造函数的参数。
Map原型方法
Map.prototype.size
返回Map 结构的成员总数
Map.prototype.set(key, value)
添加成员
Map.prototype.get(key)
查成员
Map.prototype.delete(key)
删除成员
Map.prototype.has(key)
返回一个布尔值,表示某个键是否在当前 Map 对象之中。
Map.prototype.clear()
清除所有成员
const a = {'name':'qiu'}
const map = new Map()
map.set(a,'yanxi')
map.size //1
map.get(a) //'yanxi'
map.delete(a) //true
map.clear()
// Map(0) {}
如果对同一个键多次赋值,则会覆盖原来的键
const map =new Map()
map.set(1,'123')
map.set(1,'456')
map.get(1) // '456'
需要注意的是,当键为对象类型时,我们读取它或者想覆盖它最好是使用引用的方式,而不是直接书写
const map=new Map()
map.set({name:"qiu"},'123')
map.set({name:"qiu"},'456')
map.get({name:"qiu"}) //undefined
map//Map(2) {{name:'qiu'} => "123", {name:'qiu'} => "456"}
上面的代码中,我使用map
方法分别设置以{name:'qiu'}
为对象的键名,但是实际上存在键名一致的情况,而并未出现值覆盖,并且get
读取的时候读取出undefined
,这是由于键的内存地址不同,Map不将其认同为一个键,所以我们要注意,在使用这种情况下,需要将键名转化为地址的引用
const map=new Map()
const n={name:"qiu"}
map.set(n,'123')
map.set(n,'456')
map.get(n) // '456'
由上可知,其实Map的键是和地址绑定的,只要内存地址不同,就被认为是两个不同的键。
如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。
const map=new Map()
map.set(0,'123')
map.set(-0,'456')
map //Map(1) {0 => "456"}
map.set(true,123)
map.set('true',456)
map.set(null,666)
map.set(undefined,777)
map.set(NaN,888)
map.set(NaN,999)
map //Map(6) {0 => "456", true => 123, "true" => 456, null => 666, undefined => 777, NaN => 999}
遍历方法
Map结构原生有以下方法遍历
Map.prototype.keys()
返回键名的遍历器
Map.prototype.values()
返回值的遍历器
Map.prototype.entries()
返回所有成员的遍历器
Map.prototype.forEach()
遍历所有成员
Map的遍历顺序就是插入顺序
const map=new Map([[1,'true'],[2,'false']])
for(let k of map.keys()){console.log(k)} // 1 2
for(let k of map.values()){console.log(k)} // 'true' 'false'
for(let k of map.entries()){console.log(k)} //[1, "true"][2, "false"]
map.forEach((value,key)=>{
console.log(value+':'+key)
}) // true:1 false:2
Map转化
转化为数组
使用扩展运算符能快速将Map转化为数组,同时由于Map没有Map方法,使用扩展运算符配合则可以使用filter
方法或者map
方法
const map=new Map([[1, 'one'],
[2, 'two'],
[3, 'three']])
const a=new Map([...map].filter(([key,value])=>{
return key>2
}))
a //Map(1) {3 => "three"}
转化为对象
如果所有 Map 的键都是字符串,它可以无损地转为对象,如果是非字符串,则会转为字符串后再转化成对象
const map=new Map([[8, 'one'],
[9, 'two'],
[10, 'three']])
const obj={}
map.forEach((value,key)=>{
obj[key]=value
})
obj //{8: "one", 9: "two", 10: "three"}
对象转成Map
使用Object.entries()方法可以将对象转化成成员包含键名,键值的二元数组,然后再转化成Map
//接上面代码
let o =Object.entries(obj)
// [[8, 'one'],[9, 'two'],[10, 'three']]
let map =new Map(o)
Map转成JSON
我们需要根据不同情况转成不同的JSON对象
当键名都是字符串时,可以转成对象JSON
const map=new Map()
map.set('qiu','yanxi')
map.set('height','180')
function mapToString(map){
const obj=Object.create(null)
map.forEach((value,key)=>{
obj[key]=value
})
return obj
}
const obj=mapToString(map)
JSON.stringify(obj) //"{"qiu":"yanxi","height":"180"}"
当键名不单单是字符串时,可以转成数组JSON
const map=new Map()
map.set([1,2,3],'yanxi')
map.set(['height'],'180')
JSON.stringify([...map])
//"[[[1,2,3],"yanxi"],[["height"],"180"]]"