JavaScript 中是否有像 Python 一样的字典数据结构?

145
我需要在JavaScript中创建这样一个字典:
我不记得确切的符号,但它大概是这样的:
states_dictionary={ CT=[alex,harry], AK=[liza,alex], TX=[fred, harry] ........ }

在JavaScript中有这样的东西吗?


2
请查看此问题:http://stackoverflow.com/questions/130543/can-anyone-recommend-a-good-hashtable-implementation-in-javascript - Manoj Govindan
6
你所接受的答案非常错误。 - Esben Skov Pedersen
@EsbenSkovPedersen 你在那个答案中注意到了哪些错误? - Anderson Green
我看到在我评论之后它被编辑了。似乎缺少了冒号。 - Esben Skov Pedersen
2
阅读ES6 Maps的最新答案:https://dev59.com/3HA65IYBdhLWcg3w7DT8#32993723(出于相同的原因,进行评论) - Old Badman Grey
@EsbenSkovPedersen,你怎么看去回答部分并评论哪一部分是错误的,因为我很困惑。 - rickvian
7个回答

168

这是一篇旧帖,但我仍然想提供一个图解答案。

使用JavaScript的对象表示法。就像这样:

states_dictionary={ 
     "CT":["alex","harry"], 
     "AK":["liza","alex"], 
     "TX":["fred", "harry"]
};

访问这些值的方式为:

states_dictionary.AK[0] //which is liza

或者你可以使用JavaScript的字面对象表示法,其中键不需要加引号:

states_dictionary={ 
     CT:["alex","harry"], 
     AK:["liza","alex"], 
     TX:["fred", "harry"]
};

16
值得注意的是,第一个示例应该使用完全相同的语法(除了分号)在两种语言中生成相同的对象。 states_dictionary={ "CT":["alex","harry"], "AK":["liza","alex"], "TX":["fred", "harry"] } - Denis C
我更习惯于使用字面对象表示法,因为你可以以相同的方式访问它们。那么这两者之间有什么区别呢? - John Demetriou
3
@JohnDemetriou 主要的区别在于 JavaScript 对象表示法的键必须是字符串(用双引号 " " 括起来)。对象表示法与 JSON 中的数据交换格式相同,灵感来自于字面对象表示法;值得注意的是,JSON 通常在字符串上下文中使用。 - Peter
3
实际上,Python 允许使用分号作为语句终止符号,因此第一个例子在 Python 和 JavaScript 中都是完全有效的。 - celticminstrel
1
如果值来自用户,则需要注意使用Object.hasOwnProperty.call(dictionary, key)(否则用户可以输入valueOf的值,而dictionary ['valueOf']返回属于对象原型的Object.valueOf()函数,这可能不是您的代码所期望的 - 可能会出现错误或安全问题)。 如果键不是字符串类型,则需要小心,否则隐式数字和toString转换将导致问题。 ES6 Map类型已被设计为为字典提供扩展功能。 - robocat

75

在2015年之前(即ECMAScript 6的发布之前),Javascript中没有真正的关联数组。自从那时起,您可以像Robocat所说的那样使用Map对象。在MDN上查看详细信息。例如:

let map = new Map();
map.set('key', {'value1', 'value2'});
let values = map.get('key');

如果不支持ES6,你可以尝试使用对象:

var x = new Object();
x["Key"] = "Value";

然而,对于对象来说,通常的数组属性或方法(如array.length)是不可用的。至少可以在for-in循环中访问“对象数组”。


3
性能如何?在对象中查找键是否是常数时间? - Saher Ahwal
5
由于JavaScript中o["key"]与o.key等价,因此它们的性能几乎相同。然而,性能取决于JavaScript引擎/网络浏览器。这些引擎/浏览器之间存在许多差异,特别是在旧版本中。 - Alex
1
ECMAScript 6 定义了一个官方的 Map 对象(即“在 JavaScript 中没有真正的关联数组”现在是不正确的)。 - robocat

41
在ECMAScript 6中,官方引入了Map对象,它是一个字典实现:
let dict = new Map();
dict.set("foo", "bar");

//returns "bar"
dict.get("foo");

与 JavaScript 的普通对象不同,它允许使用身份比较作为键的任何对象:

let foo = {};
let bar = {};
let dict = new Map();
dict.set(foo, "Foo");
dict.set(bar, "Bar");

//returns "Bar"
dict.get(bar);

//returns "Foo"
dict.get(foo);

//returns undefined, as {} !== foo and {} !== bar
dict.get({});

没有办法使用用户定义的键比较器,因此,如果您不想进行身份验证比较,那么您仍然只能像使用普通对象一样使用数字或字符串键。


1
对我来说可行,使用更干净的ES6方法很好。谢谢!接下来,我们是否知道任何“批量set()”的方法,例如像Python中的dict = {key: value} - Joe Sadoski

11
在这里使用JS创建了一个简单的字典:
function JSdict() {
    this.Keys = [];
    this.Values = [];
}

// Check if dictionary extensions aren't implemented yet.
// Returns value of a key
if (!JSdict.prototype.getVal) {
    JSdict.prototype.getVal = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        for (var i = 0; i < this.Keys.length; i++) {
            if (this.Keys[i] == key) {
                return this.Values[i];
            }
        }
        return "Key not found!";
    }
}


// Check if dictionary extensions aren't implemented yet.
// Updates value of a key
if (!JSdict.prototype.update) {
    JSdict.prototype.update = function (key, val) {
        if (key == null || val == null) {
            return "Key or Value cannot be null";
        }
        // Verify dict integrity before each operation
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Values[i] = val;
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}



// Check if dictionary extensions aren't implemented yet.
// Adds a unique key value pair
if (!JSdict.prototype.add) {
    JSdict.prototype.add = function (key, val) {
        // Allow only strings or numbers as keys
        if (typeof (key) == "number" || typeof (key) == "string") {
            if (key == null || val == null) {
                return "Key or Value cannot be null";
            }
            if (keysLength != valsLength) {
                return "Dictionary inconsistent. Keys length don't match values!";
            }
            var keysLength = this.Keys.length;
            var valsLength = this.Values.length;
            for (var i = 0; i < keysLength; i++) {
                if (this.Keys[i] == key) {
                    return "Duplicate keys not allowed!";
                }
            }
            this.Keys.push(key);
            this.Values.push(val);
        }
        else {
            return "Only number or string can be key!";
        }
    }
}

// Check if dictionary extensions aren't implemented yet.
// Removes a key value pair
if (!JSdict.prototype.remove) {
    JSdict.prototype.remove = function (key) {
        if (key == null) {
            return "Key cannot be null";
        }
        if (keysLength != valsLength) {
            return "Dictionary inconsistent. Keys length don't match values!";
        }
        var keysLength = this.Keys.length;
        var valsLength = this.Values.length;
        var flag = false;
        for (var i = 0; i < keysLength; i++) {
            if (this.Keys[i] == key) {
                this.Keys.shift(key);
                this.Values.shift(this.Values[i]);
                flag = true;
                break;
            }
        }
        if (!flag) {
            return "Key does not exist";
        }
    }
}

上述实现现在可以用来模拟一个字典,如下所示:
var dict = new JSdict();

dict.add(1, "one")

dict.add(1, "one more")
"Duplicate keys not allowed!"

dict.getVal(1)
"one"

dict.update(1, "onne")

dict.getVal(1)
"onne"

dict.remove(1)

dict.getVal(1)
"Key not found!"

这只是一个基本模拟。可以通过实现更好的运行时间算法使其进一步优化,以至少O(nlogn)的时间复杂度或更低的时间复杂度工作。例如,在数组上使用合并/快速排序,然后进行一些B-search查找。我没有尝试过或搜索JS中的哈希函数映射。
此外,JSdict对象的键和值可以转换为私有变量以进行偷窃。
希望这有所帮助!
编辑 >>在实施上述方案之后,我个人将JS对象用作开箱即用的关联数组。
但是,我想特别提及两种方法,这两种方法实际上对于使它成为方便的哈希表体验非常有帮助。
即:dict.hasOwnProperty(key)和delete dict [key]
阅读此帖子可作为此实施/用法的良好资源。 在JavaScript关联数组中动态创建键 谢谢!

7

使用JavaScript对象。您可以像字典中的键一样访问它们的属性。这是JSON的基础。语法类似于Python字典。参见:JSON.org


“小心”!像“watch”这样的对象原型方法可能会咬你。即使您没有任何这些,您至少会有“constructor”。 - krubo

4

一个古老的问题,但最近我需要进行AS3>JS端口,并为了速度,我编写了一个简单的AS3风格的JS字典对象:

http://jsfiddle.net/MickMalone1983/VEpFf/2/

如果你不知道,AS3字典允许你使用任何对象作为键,而不仅仅是字符串。一旦你找到了它们的用途,它们非常方便。

它的速度不如原生对象快,但在这方面我没有发现任何重大问题。

API:

//Constructor
var dict = new Dict(overwrite:Boolean);

//If overwrite, allows over-writing of duplicate keys,
//otherwise, will not add duplicate keys to dictionary.

dict.put(key, value);//Add a pair
dict.get(key);//Get value from key
dict.remove(key);//Remove pair by key
dict.clearAll(value);//Remove all pairs with this value
dict.iterate(function(key, value){//Send all pairs as arguments to this function:
    console.log(key+' is key for '+value);
});


dict.get(key);//Get value from key

1
很好用的库!我添加了一个我认为缺失的get函数,并修复了一些小的语法问题(缺少分号等)。这是修改后的fiddle链接:JSFiddle中的字典 - Matt
干得好,伙计!不知道为什么那个没有在里面! - MickMalone1983

2
Firefox 13+提供了一个实验性的map对象的实现,类似于Python中的dict对象。只能在Firefox中使用,但它比使用new Object()的属性看起来更好。文档中引用的话说:
  • Object有一个原型,因此地图中有默认键。但是,可以使用map = Object.create(null)绕过这一点。
  • Object的键是字符串,而Map的键可以是任何值。
  • 您可以轻松获取Map的大小,而必须手动跟踪Object的大小。
规范在此处

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接