创建字典类

date
Mar 20, 2023
slug
js-map-init
status
Published
tags
JavaScript数据结构与算法
Dictionary
JavaScript
summary
Create a dictionary class
type
Post

字典

​ 在字典中使用[键,值]对的形式来存储数组,键名用来查询特定元素。字典也称作映射、符号表或关联数组。
字典和集合很相似,集合以 [值,值] 的形式存储元素。
​ 将在一个 Object 的实例中存储字典中的元素,将[键,值]对保存为table[key] = {key,value}
​ 在字典中,理想的情况是用字符串作为键名,值可以是任何类型(从数、字符串等原始类型, 到复杂的对象)。但是,由于 JavaScript 不是强类型的语言,不能保证键一定是字符串。需要把所有作为键名传入的对象转化为字符串,使得从 Dictionary 类中搜索和获取值更简单。
function defaultToString(item){
	if(item === null){
        return 'NULL'
    }else if(item === undefined){
        return 'UNDEFINED'
    }else if(typeof item === 'string' || item instanceof String){
        return `${item}`
    }
    return item.toString();
}

class Dictionary{
    constructor(toStrFn = defaultToString){
        this.toStrFn = toStrFn;
        this.table = {};
    }
}
  • set(key,value):向字典中添加新元素。如果 key 已经存在,那么已存在的 value 会 被新的值覆盖。
  • remove(key):通过使用键值作为参数来从字典中移除键值对应的数据值。
  • hasKey(key):如果某个键值存在于该字典中,返回 true,否则返回 false。
  • get(key):通过以键值作为参数查找特定的数值并返回。
  • clear():删除该字典中的所有值。
  • size():返回字典所包含值的数量。与数组的 length 属性类似。
  • isEmpty():在 size 等于零的时候返回 true,否则返回 false。
  • keys():将字典所包含的所有键名以数组形式返回。
  • values():将字典所包含的所有数值以数组形式返回。
  • keyValues():将字典中所有[键,值]对返回。
  • forEach(callbackFn):迭代字典中所有的键值对。callbackFn 有两个参数:key 和 value。该方法可以在回调函数返回 false 时被中止(和 Array 类中的 every 方法相似)。

检测一个键是否存在于字典中

​ 调用 toStrFn 函数将键转化为字符串。haskey函数实现对已经存在给定键名的键值对返回 true,否则返回 false
hasKey(key){
    return this.table[this.toStrFn(key)] != null;
}

在字典和 ValuePair 类中设置键和值

​ 获取表示 key 的字符串,创建一个新的键值对并将其赋值给 table 对象上的 key 属性。如果 key 和 value 是合法的,我们返回 true,表示字典可以将 key 和 value 保存下来,否则返回 false。
class ValuePair { 
 constructor(key, value) 
    { 
 		this.key = key; 
 		this.value = value; 
 	} 
 toString() 
    { 
 		return `[#${this.key}: ${this.value}]`; 
 	} 
} 

set(key, value) 
{ 
 if (key != null && value != null)
 { 
 	const tableKey = this.toStrFn(key); 
 	this.table[tableKey] = new ValuePair(key, value); 
 	return true; 
 } 
 return false; 
}

从字典中移除一个值

​ 在字典中搜索 key。使用delete 运算符来从 items 对象中移除 key 属性。如果能够将 value 从字典中移除的话,就返回 true,否则将会返回 false。
remove(key){
    if(this.hasKey(key))
        {
            delete this.table[this.toStrFn(key)];
            return true;
        }
    return false;
}

从字典中检索一个值

方式一:访问一次 table对象
​ 在字典中查找一个特定的 key,并检索它的 value。
get 方法首先会检索存储在给定 key 属性中的对象,如果 valuePair 对象存在, 将返回该值,否则将返回一个 undefined 值。
get(key){
    const valuePair = this.table[this.toStrFn(key)];
    return valuePair == null ? undefined : valuePair.value;
}
方式二:访问两次 table对象
第一次 是在 hasKey 方法中,第二次是在 if 语句内。
get(key){
    if(this.hasKey(key))
        {
            return this.table[this.toStrFn(key)];
        }
    return undefined;
}

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

方式一:使用Object类内置的values方法
keyValues() { 
 return Object.values(this.table); 
}
方式二
​ 迭代了 table 对象的所有属性,为了保证 key 是存在的,使用 hasKey 函数来进行检验,然后将 table 对象中的 valuePair 加入结果数组。
​ 在该方法里,由于我们已经直接从 table 对象中获取了属性(key),不需要用 toStrFn 函数将 它转化为字符串。
keyValues(){
    const valuePairs = [];
    for(const k in this.table)
    {
        if(this.hasKey(k))
            {
                valuePairs.push(this.table[k]);
            }
    }
    return valuePairs;
}

返回识别值的所有键名

​ 使用创建好的 keyValues 方法返回一个包含 valuePair 实例得数组,后迭 代每个 valuePair。我们需要查找原始键名,所以只返回它的 key。
keys(){
    return this.keyValues().map( valuePair => valuePair.key);
}
map函数的逻辑如下:
const keys = [];
const valuePairs = this.keyValues();
for(let i = 0; i < valuePairs.length; i++){
    keys.push(valuePairs[i].key);
}
return keys;

字典中所有值构成的数组

values(){
    return this.keyValues().map(valuePair => valuePair.value)
}

用 forEach 迭代字典中的每个键值对

获取字典中所有 valuePair 构成的数组,迭代每个 valuePair 并执行以参数形式传入 forEach 方法的 callbackFn 函数保存结果
forEach(callbackFn) { 
 const valuePairs = this.keyValues(); 
 for (let i = 0; i < valuePairs.length; i++)
 {  
 	const result = callbackFn(valuePairs[i].key, valuePairs[i].value); 
 	if (result === false)
    { 
 		break; 
 	} 
 } 
}
如果回调函数返回了 false,中断 forEach 方法的执行,打断正 在迭代 valuePairs 的 for 循环。

clear、size、isEmpty 和 toString 方法

size

size 方法返回字典中的值的个数。
size() {
    return Object.keys(this.table).length;
  }

isEmpty

检验字典是否为空
isEmpty(){
    return this.size() === 0;
}

clear

清空字典内容
clear(){
    this.table = {};
}
 
接下来使用创建的方法对字典进行测试
function defaultToString(item) {
  if (item === null) {
    return "NULL";
  } else if (item === undefined) {
    return "UNDEFINED";
  } else if (typeof item === "string" || item instanceof String) {
    return `${item}`;
  }
  return item.toString();
}

class ValuePair {
  constructor(key, value) {
    this.key = key;
    this.value = value;
  }
  toString() {
    return `[#${this.key}: ${this.value}]`;
  }
}

class Dictionary {
  constructor(toStrFn = defaultToString) {
    this.toStrFn = toStrFn;
    this.table = {};
  }
  hasKey(key) {
    return this.table[this.toStrFn(key)] != null;
  }
  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(key) {
    if (this.hasKey(key)) {
      delete this.table[this.toStrFn(key)];
      return true;
    }
    return false;
  }
  get(key) {
    const valuePair = this.table[this.toStrFn(key)];
    return valuePair == null ? undefined : valuePair.value;
  }
  keyValues() {
    return Object.values(this.table);
  }
  keys() {
    return this.keyValues().map((valuePair) => valuePair.key);
  }
  values() {
    return this.keyValues().map((valuePair) => valuePair.value);
  }
  forEach(callbackFn) {
    const valuePairs = this.keyValues();
    for (let i = 0; i < valuePairs.length; i++) {
      const result = callbackFn(valuePairs[i].key, valuePairs[i].value);
      if (result === false) {
        break;
      }
    }
  }
  size() {
    return Object.keys(this.table).length;
  }
  isEmpty() {
    return this.size() === 0;
  }
  clear() {
    this.table = {};
  }
}

const dictionary = new Dictionary();
dictionary.set("Gandalf", "gandalf@email.com");
dictionary.set("John", "johnsnow@email.com");
dictionary.set("Tyrion", "tyrion@email.com");

console.log(dictionary.hasKey("Gandalf")); //true
console.log(dictionary.size()); //3

console.log(dictionary.keys()); //[ 'Gandalf', 'John', 'Tyrion' ]
console.log(dictionary.values()); //[ 'gandalf@email.com', 'johnsnow@email.com', 'tyrion@email.com' ]
console.log(dictionary.get("Tyrion")); //tyrion@email.com

dictionary.remove("John");
dictionary.forEach((k, v) => {
  console.log("forEach: ", `key: ${k}, value: ${v}`);
});
/**forEach:  key: Gandalf, value: gandalf@email.com
forEach:  key: Tyrion, value: tyrion@email.com */
 

© shallrise 2024