Javascript中键/值对的对象与数组


83

假设您有一个非常简单的数据结构:

(personId, name)

...并且您想将其中一些存储在javascript变量中。如我所见,您有三个选择:

// a single object
var people = {
    1 : 'Joe',
    3 : 'Sam',
    8 : 'Eve'
};

// or, an array of objects
var people = [
    { id: 1, name: 'Joe'},
    { id: 3, name: 'Sam'},
    { id: 8, name: 'Eve'}
];

// or, a combination of the two
var people = {
    1 : { id: 1, name: 'Joe'},
    3 : { id: 3, name: 'Sam'},
    8 : { id: 8, name: 'Eve'}
};

如果您要存储(或期望您可能拥有)多个“价值”部分(例如,增加他们的年龄等),那么第二或第三种选择显然是可行的方法,因此,为了论证,假设此结构永远不再需要任何数据值。您选择哪一个,为什么?


编辑:该示例现在显示最常见的情况:非顺序ID。


1
还有一个2d数组:(var people = [[1, 'Joe'],[3, 'Sam'],[8,'Eve']];它不能让您通过id快速查找,但是是存储数据的另一种选择)
用户

Answers:


58

每个解决方案都有其用例。

我认为,如果您要定义一对一的关系(例如简单的映射),则第一种解决方案是好的,尤其是当您需要使用该键作为查找键时。

总的来说,第二种解决方案对我来说最健壮,如果我不需要快速查找键,我可能会使用它:

  • 它是自描述的,因此您不必依赖任何使用 人员的人就知道密钥是用户的ID。
  • 每个对象都是独立的,这更适合将数据传递到其他地方-而不是您随便传递两个参数(id和name)。
  • 这是一个罕见的问题,但是有时键值可能无效,无法用作键。例如,我曾经想映射字符串转换(例如,“:”到“>”),但是由于“:”不是有效的变量名,因此我不得不使用第二种方法。
  • 它很容易扩展,以防万一您需要向某些(或全部)用户添加更多数据。(对不起,我了解您的“为争辩”,但这是一个重要方面。)

如果您需要快速的查找时间+上面列出的一些优点(传递数据,自我描述),那么第三种方法会很好。但是,如果您不需要快速的查找时间,则麻烦得多。同样,无论哪种方式,如果对象中的id与people中的id有所不同,都将有出错的风险。


1
在你的第三点。可以使用括号表示法访问对象属性来避免该问题:var x = {“ ab> c ++”:“ foo”}; alert(x ['ab> c ++']);
尼克

我相信您确实有一些带有属性的保留字的问题。但是使用数字时没有问题。
derobert

4
@derobert:我不这么认为。x = {“ delete”:1,“ for”:2,“ function”:3}; -这是有效的,并且可以通过相同的方式进行访问。
尼克

7

实际上,有第四种选择:

var people = ['Joe', 'Sam', 'Eve'];

因为您的值碰巧是连续的。(当然,您必须添加/减去一个---或仅将undefined作为第一个元素)。

就我个人而言,我会选择您的(1)或(3),因为这些将是通过ID查找某人的最快方法(最糟糕的情况是O log n)。如果必须在(2)中找到id 3,则可以按索引查找它(在这种情况下,我的(4)可以),或者必须搜索— O(n)。

澄清:我说O(log n)可能是最糟糕的,因为AFAIK和实现可以决定使用平衡树而不是哈希表。假设冲突最少,则哈希表为O(1)。

从nickf编辑:从那以后,我在OP中更改了示例,因此此答案可能不再那么有意义。道歉。

编辑后

好的,后期编辑,我选择选项(3)。它是可扩展的(易于添加新属性),具有快速查找功能,并且还可以进行迭代。如果需要,它还允许您从输入回到ID。

如果(a)您需要节省内存,则选项(1)很有用;(b)您永远不需要从对象回到id;(c)您将永远不会扩展存储的数据(例如,您不能添加此人的姓氏)

如果您(a)需要保留顺序,则选项(2)很好。(b)需要迭代所有元素;(c)不需要按id查找元素,除非按id排序(您可以在O(log n)中进行二进制搜索。请注意,当然,如果需要保持对其进行排序,则需要付费插入成本。


我知道为什么(1)和(3)比(2)更快,但是我想知道您现在是如何得到O(log(n))的?不应该是O(1),因为ID应该存储在哈希表中吗?
纳撒尼尔·赖因哈特

我将log n设置为最差,因为我可以看到一个实现决定使用平衡树而不是哈希表。当然,哈希表将为O(1)[假设冲突最少]。
德罗伯特

这些ID并不总是那么简单,它们很少是顺序IRL,这就是为什么您需要指定它们的原因。我确实承认这是一个糟糕/模棱两可的例子。
尼克

+1,您的答案并不比接受的答案差……但您的第四个解决方案并未存储所有数据,因此这里是正确的第四个选项:var people = []; people[1] = 'Joe'; people[3] = 'Sam'; people[8] = 'Eve';或作为二维数组:(var people = []; people[1] = ['Joe', 'Smith']; people[3] = ['Sam', 'Miller']; people[8] = ['Eve', 'Sweet'];例如,“ people [ 3] [1];'可以很好地用于任何类内部的快速内部数据存储-很好,在类中,我的意思是Javascripter不喜欢的“ new WhateverFunction();”;我做;-)...
Marcus

...但是这仅在id是整数的情况下有效,否则“经典”解决方案2(连同for(var key in object)用于访问的著名方法)是在正常情况下(它具有O(n / 2))或解决方案的方法3,如果你有更快速的访问非常大的阵列,因为它有O(1)...终于让我们忘了上述方案1,不仅是如果你的ID是一个整数,而且万一ID不整:var people = {'Smith':'Joe', 'Miller':'Sam', 'Sweet': 'Eve' };(因为您需要像这样访问您的对象:alert(people.Smith);真的不是很好!)。
Marcus

2

假设数据永远不会改变,则第一个(单个对象)选项是最佳选择。

结构的简单性意味着它是最快的解析方法,并且在这种数据集很少,很少(或从不改变)的情况下,例如我,我只能想象它会被频繁执行-在这种情况下,开销最小要走的路。



1

第三个选项是任何前瞻性应用程序的最佳选择。您可能希望将更多字段添加到您的个人记录,因此第一个选项不合适。另外,您很有可能要存储大量人员,并且希望快速查找记录-因此将它们转储到一个简单的数组中(如在选项2中所做的那样)也不是一个好主意。

第三种模式使您可以选择使用任何字符串作为ID,具有复杂的Person结构并在固定时间内获取和设置Person记录。这绝对是要走的路。

选项#3缺少的一件事是稳定的确定性排序(这是选项#2的优势)。如果需要,我建议保留一个有序的人员ID数组作为单独的结构,以便在需要顺序列出人员时使用。好处是,您可以为同一数据集的不同顺序保留多个此类数组。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.