学习JavaScript数据结构与算法(六)— 字典

308 阅读2分钟

前言

本人是一个刚入行的菜鸡前端程序员,写这个文章的目的只是为了记录自己学习的笔记与成果,如有不足请大家多多指点。 在集合中,我们感兴趣的是每个值本身,并把它当作主要元素。在字典(或映射)中,我们用[键,值]对的形式来存储数据。

字典

我们已经知道,集合表示一组互不相同的元素(不重复的元素)。在字典中,存储的是[键,值]对,其中键名是用来查询特定元素的。字典和集合很相似,集合以[值,值]的形式存储元素,字典则是以[键,值]的形式存储元素。字典也称作映射符号表关联数组

创建字典类

与set相似,ECMAscript2015同样包含了一个 Map 类的实现,即我们所说的字典。
我们要实现的类就是以 Map 类的实现为基础的。你会发现它和set类很相似,但不同于[值,值]对的形式,我们要存储的是[键,值]对。

//首先声明一个 defaultToString 函数,用来保证所有的键名都是字符串  
function defaultToString(item) {
    if(item === null) {
        return 'NULL'
    } else if (item === undefined) {
        return 'UNDEFINED'
    } else if ( typeof item === 'string' || items instanceof String) {
        return `${item}`
    }
    return item.toString()
}

//声明字典类  
class Dictionary() {
    constructor(toStrFn = defaultToString) {
        this.toStrFn = toStrfn
        this.table ={}
    }
}
// 声明一个 ValuePair 类 
class ValuePair {
    constructor (key, value) {
        this.key = key
        this.value = value
    }
    toString() {
        return `[#${this.key}: ${this.value}]`
    }
}

为了在字典中保存value,我们将key转化为了字符串,而为了保存信息的需要,我们同样要保存原始的 key。因此,我们不是只将 value 保存在字典中,而是要保存两个值:原始的key 和 value。为了字典能更简单的通过 toString 方法输出结果,我们同样要为 ValuePair 类创建 toString 方法。

检测一个键是否存在于字典中 - hasKey()
hasKey(key) {
    return this.table(this.toStrFn(key)) != null
}
在字典和 ValutPair 类中设置键和值 - set()
set(key, value) {
    if(key != null && value != null) {
        const tableKey = this.toStrFn(key)
        this.table[tableKey] = new ValuePair(key, value)
        return true
    }
    return false
}
从字典中移除一个值 - remove()
remove(key) {
    if (this.hasKey(key)) {
        delete this.table[this.toStrFn(key)]
        return true
    }
    return false 
}
从字典中检索一个值 - get()
get(key) {
    const valuePair = this.table[this.toStrFn(key)]
    return valuePair == null ? undefined : valuePair.value()
}

我们也可以先验证我们要获取的value是否存在,如果存在,我们就在 table 对象中找到它并返回。

get(key) {
    if(this.hasKey(key)) {
        return this.table[this.toStrFn(key)]
    }
    return undefined
}

但是在第二种方法中,我们会获取两次 key 的字符串以及访问两次 table 对象。第一种方式直接返回了value值,第二种方式返回的是一个对象。所以第一种方式的消耗更少,效率更高。

keys、values 和 valuePairs 方法

我们已经给 Dictionary 类创建了最重要的方法,现在来创建一些很有用的辅助方法。

keyValues()

以数组的形式返回字典中的所有 valuePair 对象。

keyValues() {
    return Object.values(this.table)
}

我们也可以这样写

keyValues() {
    const valuePair = [] 
    for (const k in this.table) {
        if(this.hasKey(k)) {
            valuePair.push(this.table[k])
        }
    }
    return valuePairs
}
keys()

返回Dictionary类中用于识别值得所有(原始)键名

keys() {
    return this.keyValues().map(ValuePair => valuePair.key)
}
values()

返回ValuePair类的value属性。

values() {
    return this.keyValues().map( valuePair => valuePair.value)
}
clear、size、isEmpty 和 toString 方法
size() - 返回字典中的值的个数
size() {
    return Object.keys(this.table).length
}
isEmpty() - 检查字典是否为空
isEmpty() {
    return this.size() === 0 
}
clear() - 清空字典
clear() {
    this.table = {}
}
toString()
toString() {
    if(this.isEnpty()) {
        return ''
    }
    const valueParis = this.keyValue()
    let objString = `${valuePairs[0].toString()}`
    for(let i = 0; i < valuePairs.length; i++) {
        objString = `${objString},${valuePairs[i].toString}`
    }
    return objString
}
ES2015 Map 类

ECMAscript2015 新增了Map类。可以基于ES2015的 Map 类开发我们的 Dictionary 类。

const map = new Map()  
map.set('Gandalf', 'gandalf@email.com')
map.set('John', 'johnsnow@email.com')
map.set('Tyrion', 'tyrion@email.com')

console.log(map.has('Gandalf')) //true
console.log(map.size) //3
console.log(map.keys()) // {"Gandalf", "John", "Tyrion"}
console.log(map.values()) //{"gandalf@email.com", "johnsnow@email.com", "tyrion@email.com"}
console.log(map.get('Tyrion')) //tyrion@email.com

和我们自己写的 Dictionary 类不同,ES2015 的 Map 类的 values 方法和 keys 方法都返回 Iterator ,而不是值或键构成的数组。另一个区别是,我们实现的 size 方法返回字典中存储元素的个数,而 Map 类则有一个 size 属性。
删除 map 中的元素可以用 delete 方法

map.delete('John')

clear 方法会重置 map 数据结构。

小结

在本篇中,我们学习了字典的相关知识,了解了如何添加、移除和获取元素以及其他一些方法。我们还了解了字典和集合的不同之处。我们还学习了如何使用 ES2015 的 Map 类。
在下一篇中,我们将学习递归。