javascript基础
Map and Set(映射和集合)
map
是一个带键的数据项的集合,就像一个 Object
一样。 但是它们 的差别是 Map
允许任何类型的键(key)。
它的方法和属性如下:
-
new Map()
—— 创建 map。 -
map.set(key, value)
—— 根据键存储值。返回 map 自身。 -
map.get(key)
—— 根据键来返回值,如果map
中不存在对应的key
,则返回undefined
。 -
map.has(key)
—— 如果key
存在则返回true
,否则返回false
。 -
map.delete(key)
—— 删除指定键的值。 -
map.clear()
—— 清空 map。 -
map.size
—— 返回当前元素个数。
如果要在 map
里使用循环,可以使用以下三个方法:
map.keys()
—— 遍历并返回所有的键(returns an iterable for keys),map.values()
—— 遍历并返回所有的值(returns an iterable for values),map.entries()
—— 遍历并返回所有的实体(returns an iterable for entries)[key, value]
,for..of
在默认情况下使用的就是这个。
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 遍历所有的键(vegetables)
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// 遍历所有的值(amounts)
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// 遍历所有的实体 [key, value]
for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
alert(entry); // cucumber,500 (and so on)
}
// 对每个键值对 (key, value) 运行 forEach 函数
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 500 etc
});
set
是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
var set = new Set([1,2,3]); // {1,2,3}
var arr = Array.from(set);//[1,2,3]
它的主要方法如下:
-
new Set(iterable)
—— 创建一个set
,如果提供了一个iterable
对象(通常是数组),将会从数组里面复制值到set
中。 -
set.add(value)
—— 添加一个值,返回 set 本身 -
set.delete(value)
—— 删除值,如果value
在这个方法调用的时候存在则返回true
,否则返回false
。 -
set.has(value)
—— 如果value
在 set 中,返回true
,否则返回false
。 -
set.clear()
—— 清空 set。 -
set.size
—— 返回元素个数。
如果要在 set 里使用循环,可以使用以下三个方法:
set.keys()
—— 遍历并返回所有的值(returns an iterable object for values),set.values()
—— 与set.keys()
作用相同,这是为了兼容Map
,set.entries()
—— 遍历并返回所有的实体(returns an iterable object for entries)[value, value]
,它的存在也是为了兼容Map
。
树形数据
深度优先算法(dfs)
遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。
最后得出的结果为:ABDECFHG。
深度优先算法-递归
function treeIterator(tree: any[], func: (arg0: any) => boolean) {
tree.forEach((node) => {
if(!func(node)){
return;
}
node.children && treeIterator(node.children, func)
})
}
广度优先算法(bfs)
遍历规则:
-
先访问完当前顶点的所有邻接点。(应该看得出广度的意思)
-
先访问顶点的邻接点先于后访问顶点的邻接点被访问。
最后得出的结果为:ABCDEFGH。
广度优先算法 利用队列先进先出的方式进行遍历。
function treeIteratorBfs(tree:any[], func: (arg0: any) => boolean) { let node, nodeQueue = [...tree] while ((node = nodeQueue.shift())) { if(!func(node)){ return; } node.children && nodeQueue.push(...node.children) } }
N叉树的最大深度–广度优先
function treeIteratorMaxDepth(tree:any[]) {
let node = tree[0], index = 1;
while(node.children.length>0){
index++;
const children = node.children.reduce((pre: any[],cur: { children: any; })=>{
cur.children && (pre = pre.concat(cur.children))
return pre
},[])
node = {children};
}
return index;
}
思考
1、Map 和 Object 的区别是什么,什么情况下需要使用 Map ?
Map 和 Object 的区别
- 键:Object遵循普通的字典规则,键必须是单一类型,并且只能是整数、字符串或是Symbol类型。但在Map中,key可以为任意数据类型(Object, Array等)。
- 元素顺序:Map会保留所有元素的顺序,而Object并不会保证属性的顺序。
- 继承:Map是Object的实例对象,而Object显然不可能是Map的实例对象。
什么情况下需要使用 Map(映射)
- Map是一个纯哈希结构,而Object不是(它拥有自己的内部逻辑)。使用
delete
对Object的属性进行删除操作存在很多性能问题。所以,针对于存在大量增删操作的场景,使用Map更合适。 - 不同于Object,Map会保留所有元素的顺序。Map结构是在基于可迭代的基础上构建的,所以如果考虑到元素迭代或顺序,使用Map更好,它能够确保在所有浏览器中的迭代性能。
- Map在存储大量数据的场景下表现更好,尤其是在key为未知状态,并且所有key和所有value分别为相同类型的情况下。
- 获取对象的键值对的个数,建议map。map有size属性。
2、Set 和 Array 的区别是什么,什么情况下需要使用 Set ?
Set 和 Array 的区别
它们之间最大的差别就是Array中的元素是可以重复的,而Set中的元素不可重复。除此之外,Array被认为是一种索引集合,而Set是一种键的集合。
- 索引集合是指按下标作为索引值排序的数据结构
- 键的集合使用key访问元素,访问顺序与元素插入顺序一致。
什么情况下需要使用 Set
- 数组去重
- 获取两个集合的并集,交集,差集。
- 判断两个集合是否为子集或超集。
3、不用递归如何实现树的深度优先(BFS)搜索?
使用递归方法实现对树的遍历效率非常低,下面利用栈的特性来实现对树的深度优先遍历;
function dfsD(headerNode: any| any[], func: (arg0: any) => boolean){
const stack: any[] = Array.isArray(headerNode)
? headerNode.reverse(): [headerNode];
while (stack.length > 0) {
const node = stack.pop();
func(node);
if (Array.isArray(node.children)) {
stack.push(...node.children.reverse());
}
}
}
4、JSON.prase 和 JSON.stringify 有哪些缺点?
JSON.prase 和 JSON.stringify可以实现深拷贝。
- 如果obj里面存在时间对象,JSON.parse(JSON.stringify(obj))之后,时间对象变成了字符串。
- 如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象。
- 如果obj里有属性值是函数,undefined,则序列化的结果该属性丢失。
- 如果obj里有属性值是NaN、Infinity和-Infinity,则序列化的结果会变成null。
- JSON.stringify()只能序列化对象的可枚举的自有属性。如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor。
- 如果对象中存在循环引用的情况也无法正确实现深拷贝。