o4YBAF4WgfiAZoR6AABlyU2LENM722.jpg

什么是字典

字典是一种以 [键,值] 对存储数据的数据结构。在字典中,键名是用来查询特定元素的。字典也称作映射符号表关联数组。
**

实现字典

定义 ValuePair 类

  1. class ValuePair {
  2. constructor(key, value) {
  3. this.key = key;
  4. this.value = value;
  5. }
  6. toString() {
  7. const key = typeof this.key === 'object' ? JSON.stringify(this.key) : this.key
  8. return `[#${key}: ${this.value}]`;
  9. }
  10. }

ValuePair 类将原始的 key 和 value 转换成字符串,便于我们同时将原始的 key 和 value 保存在字典中

定义 defaultToString 函数

  1. const defaultToString = (item) => {
  2. if (item === null) {
  3. return 'NULL';
  4. } else if (item === 'undefined') {
  5. return 'UNDEFINED';
  6. } else if (typeof item === 'string' || item instanceof String) {
  7. return `${item}`;
  8. } else if (typeof item === 'object') {
  9. return JSON.stringify(item);
  10. }
  11. return item.toString();
  12. }

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),传统上只能用字符串作为键。因此,我们需要定义一个转换函数,将所有作为键名传入的对象转化为字符串,使得从 Dictionary 类中搜索和获取值更简单。

定义 Dictionary 类

  1. class Dictionary {
  2. constructor(toStrFn = defaultToString) {
  3. this.toStrFn = toStrFn;
  4. this.table = {};
  5. }
  6. }

在 Dictionary 类中,我们使用 Object 的实例来存储字典中的元素,我们会将 [键, 值] 对保存为 table[key] = { key, value }的形式。

下面,我们为字典定义一些可用的方法:
set(key, value) 向字典中添加新元素
remove(key) 移除健值对应的数据值
hasKey(key) 检查某个健值是否存在于字典中
get(key) 获取字典中的某个健值
clear() 清空字典中的所有值
size() 返回字典中元素的数量
isEmpty() 判断字典是否为空字典
keys() 以数组形式返回字典中的所有键名
values() 以数组的形式返回字典中的所有健值
keyValues() 返回字典中的所有 [键, 值] 对
forEach(callbackFn) 迭代字典中的所有 健值对
toString() 返回字典内容的字符串形式

下面,我们逐一实现字典的方法:

set(key, value) 向字典中添加新元素

  1. set(key, value) {
  2. // key/value 不为 null 或 undefined,将 key/value 添加到字典中
  3. if (key != null && value != null) {
  4. // 获取表示 key 的字符串
  5. const tableKey = this.toStrFn(key);
  6. // 创建一个新的健值对,将其保存到 table 对象上
  7. this.table[tableKey] = new ValuePair(key, value);
  8. // 返回 true 表示key/value已添加到字典中
  9. return true;
  10. }
  11. // 返回 false 表示没有将 key/value 添加到字典中
  12. return false
  13. }

set 方法用于向字典中添加新的元素或者更新已有的元素。

set 方法接收 key 和 value 作为参数,如果 key 和 value 不是 undefined 或 null,那么我们获取表示 key 的字符串,创建一个新的健值对并将其赋值给 table 对象上的 key 属性,并返回 true,表示key和value已经添加到字典中;否则返回false,表示没有将key和value添加到字典中

remove(key) 移除健名对应的数据值

  1. remove(key) {
  2. if (this.hasKey(key)) {
  3. // delete this.table[this.toStrFn(key)];
  4. // Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性
  5. Reflect.deleteProperty(this.table, this.toStrFn(key));
  6. // 返回 true 表示健值移除成功
  7. return true;
  8. }
  9. // 键名不存在
  10. return false;
  11. }

removeKey 方法根据键名来移除字典中对应的健值。

我们可以使用 delete 运算符来从 table对象中删除 key 属性,也可以使用 Reflect 对象的 deleteProperty 方法来删除 table 对象中的 key 属性,Reflect.deleteProperty 方法等同于 delete obj[name]。如果能将 value 从字典中删除,就返回 true,否则就返回 false。

hasKey(key) 检查某个健值是否存在于字典中

  1. hasKey(key) {
  2. return this.table[this.toStrFn(key)] != null;
  3. }

hasKey 方法会检查一个给定键名的健值对是否存在于字典中,如果存在,返回 true,否则返回 false

get(key) 获取字典中的某个健值

  1. get(key) {
  2. const valuePair = this.table[this.toStrFn(key)];
  3. return valuePair == null ? undefined : valuePair.value;
  4. }

get 方法在字典中查找一个给定的key,并将该 key 对应的 value 返回。

get 方法首先会检索给定的key在字典中是否存在,如果该key对应的 valuePair 对象存在,则返回该值,否则返回一个 undefined 值。

clear() 清空字典中的所有值

  1. clear() {
  2. this.table = {}
  3. }

clear 方法用于清空字典中的所有值。要清空字典的内容,我们只需要将 table 对象重新赋值为空对象即可。

size() 返回字典中元素的数量

  1. size() {
  2. return Object.keys(this.table).length;
  3. }

size 方法返回字典中值的个数。我们可以使用 Object.keys 方法来获取 table 对象中的所有键名,然后通过数组的 length 属性获取个数。

isEmpty() 判断字典是否为空字典

  1. isEmpty() {
  2. return this.size() === 0;
  3. }

size 方法判断字典是否为空。检验字典是否为空,我们可以获取它的 size 是否为 0,如果 size 为 0,表示字典为空。

keys() 以数组形式返回字典中的所有键名

  1. keys() {
  2. return this.keyValues().map(valuePair => valuePair.key);
  3. }

key 方法返回字典中的所有原始键名。在 keys 方法中,我们调用 keyValues 方法获取所有的健值,然后迭代每一个健值 valuePair,返回存储在 valuePair 中的原始键名。

values() 以数组的形式返回字典中的所有健值

  1. values() {
  2. return this.keyValues().map(valuePair => valuePair.value);
  3. }

values 方法返回字典中的所有原始value值。在 values 方法中,我们调用 keyValues 方法获取字典中存储的所有健值,然后迭代每一个健值 valuePair,返回存储在 valuePair 中的原始 value 值。

keyValues() 返回字典中的所有 [键, 值] 对

  1. keyValues() {
  2. const valuePairs = [];
  3. // 迭代 table 对象
  4. for (let k in this.table) {
  5. if (this.hasKey(k)) {
  6. // 将 table 对象中的值添加到数组中
  7. valuePairs.push(this.table[k]);
  8. }
  9. }
  10. return valuePairs;
  11. }

keyValues 方法返回字典中存储的所有健值。在方法中,我们迭代 table 对象,如果 key 对应的值存在,我们将值添加到数组 valuePairs 中。

forEach(callbackFn) 迭代字典中的所有 健值对

  1. forEach(callbackFn) {
  2. // 获取字典中所有的健值
  3. const valuePairs = this.keyValues();
  4. for(let i = 0; i < valuePairs.length; i++) {
  5. // 迭代 每个健值,执行 callbackFn 回调函数
  6. const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
  7. if (result === false) {
  8. // 中断 forEach 方法的执行,并打断迭代 valuePairs 的 for 循环
  9. break;
  10. }
  11. }
  12. }

forEach 方法迭代字典中的所有健值对。forEach 方法的参数 callbackFn是一个回调函数, 有两个参数:key 和 value。

首先,我们获取字典中存储的所有健值 valuePairs,然后迭代每个健值 valuePair 并执行传入的参数 callbackFn 回调函数,callbackFn将会返回存储在 valuePair 中的原始健名和原始健值。如果 callbackFn 函数调用时返回了 false,我们会中断 forEach 方法的执行,打断正在迭代 valuePairs 的 for 循环。

toString() 返回字典内容的字符串形式

  1. toString() {
  2. if (this.isEmpty()) {
  3. return '';
  4. }
  5. const valuePairs = this.keyValues();
  6. let objString = `${valuePairs[0].toString()}`;
  7. for (let i = i; i < valuePairs.length; i++) {
  8. objString = `${objString}, ${valuePairs[i].toString()}`;
  9. }
  10. return objString;
  11. }

在 toString 方法中,如果字典为空,我们就返回一个空字符串。如果不是,我们迭代字典中的所有健值,调用 toString 方法将每个健值转换为字符串。最后在方法末尾将代表字典内容的字符串返回。

完整代码

  1. class ValuePair {
  2. constructor(key, value) {
  3. this.key = key;
  4. this.value = value;
  5. }
  6. toString() {
  7. const key = typeof this.key === 'object' ? JSON.stringify(this.key) : this.key
  8. return `[#${key}: ${this.value}]`;
  9. }
  10. }
  11. const defaultToString = (item) => {
  12. if (item === null) {
  13. return 'NULL';
  14. } else if (item === 'undefined') {
  15. return 'UNDEFINED';
  16. } else if (typeof item === 'string' || item instanceof String) {
  17. return `${item}`;
  18. } else if (typeof item === 'object') {
  19. return JSON.stringify(item);
  20. }
  21. return item.toString();
  22. }
  23. class Dictionary {
  24. constructor(toStrFn = defaultToString) {
  25. this.toStrFn = toStrFn;
  26. this.table = {};
  27. }
  28. // 向字典中添加新元素
  29. set(key, value) {
  30. if (key != null && value != null) {
  31. const tableKey = this.toStrFn(key);
  32. this.table[tableKey] = new ValuePair(key, value);
  33. return true;
  34. }
  35. return false;
  36. }
  37. // 移除键名对应的数据值
  38. remove(key) {
  39. if (this.hasKey(key)) {
  40. Reflect.deleteProperty(this.table, this.toStrFn(key));
  41. return true;
  42. }
  43. return false;
  44. }
  45. // 检查某个键值是否存在于字典中
  46. hasKey(key) {
  47. return this.table[this.toStrFn(key)] != null;
  48. }
  49. // 获取字典中的某个键值
  50. get(key) {
  51. const valuePair = this.table[this.toStrFn(key)];
  52. return valuePair == null ? undefined : valuePair.value;
  53. }
  54. // 清空字典中的所有值
  55. clear() {
  56. this.table = {};
  57. }
  58. // 返回字典中所有元素的数量
  59. size() {
  60. return Object.keys(this.table).length;
  61. }
  62. // 判读字典是否为空
  63. isEmpty() {
  64. return this.size() === 0;
  65. }
  66. // 以数组形式返回字典中的所有键名
  67. keys() {
  68. return this.keyValues().map(valuePair => valuePair.key);
  69. }
  70. // 以数组形式返回字典中的所有健值
  71. values() {
  72. return this.keyValues().map(valuePair => valuePair.value);
  73. }
  74. // 返回字典中的所有 [健, 值] 对
  75. keyValues() {
  76. return Object.values(this.table);
  77. }
  78. // 迭代字典中的所有健值对
  79. forEach(callbackFn) {
  80. const valuePairs = this.keyValues();
  81. for (let i = 0; i < valuePairs.length; i++) {
  82. const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
  83. if (result === false) {
  84. break;
  85. }
  86. }
  87. }
  88. // 返回字典内容的字符串形式
  89. toString() {
  90. if (this.isEmpty()) {
  91. return '';
  92. }
  93. const valuePairs = this.keyValues();
  94. let objString = `${valuePairs[0].toString()}`;
  95. for (let i = 1; i < valuePairs.length; i++) {
  96. objString = `${objString}, ${valuePairs[i].toString()}`;
  97. }
  98. return objString;
  99. }
  100. }

ES6 中的Map

Map 是 ES6 提供的一种数据结构,它类似于对象,是键值对的集合,但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。我们可以将 ES6 理解为我们在上面实现的字典。

Map 实例的属性及方法:

  • size:返回Map结构的成员数量
  • get():返回键值对
  • set():添加键值对,返回实例
  • delete():删除键值对,返回布尔
  • has():检查键值对,返回布尔
  • clear():清除所有成员
  • keys():返回以键为遍历器的对象
  • values():返回以值为遍历器的对象
  • entries():返回以键和值为遍历器的对象
  • forEach():使用回调函数遍历每个成员