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

IT技术 javascript data-structures
2021-02-09 05:20:18

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

(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'}
};

如果您有(或预计您可能有)多个“value”部分要存储(例如,添加他们的年龄或其他东西),那么第二个或第三个选项显然是要走的路,所以,为了争论,让我们假设这个结构中永远不需要更多的数据值。你选择哪一个,为什么?


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

6个回答

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

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

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

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

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

关于你的第三点。可以用括号表示法访问对象属性以避免该问题: var x = { "ab>c++" : "foo"}; 警报(x['ab>c++']);
2021-03-19 05:20:18
@derobert:我不这么认为。x = {“删除”:1,“为”:2,“功能”:3};-- 这是有效的,可以以相同的方式访问。
2021-03-21 05:20:18
我相信,您确实遇到了一些带有属性的保留字的问题。但是当你使用数字时没有问题。
2021-03-25 05:20:18

其实还有第四种选择:

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 中进行二分查找)。注意,当然,如果您需要保持排序,那么您将支付插入成本。

我将 log n 评为最差,因为我可以看到一个实现决定使用平衡树而不是哈希表。当然,哈希表将是 O(1) [假设冲突最小]。
2021-03-18 05:20:18
+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];' 这对于任何类中的快速内部数据存储都非常有效 - 好吧,对于类,我的意思是“新的 WhatFunction();” Javascripters 不喜欢的东西,我这样做 ;-)...
2021-03-23 05:20:18
ID 并不总是那么简单,而且它们很少是顺序 IRL,这就是您需要指定它们的原因。我承认这是一个糟糕/模棱两可的例子。
2021-03-24 05:20:18
我明白为什么 (1) 和 (3) 比 (2) 快,但我想知道你是如何得到 O(log(n)) 的?不应该是 O(1),因为 id 应该存储在哈希表中吗?
2021-03-25 05:20:18
...但这仅在 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);这不是很好!)。
2021-04-02 05:20:18

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

结构的简单意味着它是最快的解析,并且在像这个这样的小,很少(或从不)变化的数据集的情况下,我只能想象它会经常执行 - 在这种情况下最小的开销是要走的路。

我创建了一个小库来管理键值对。

https://github.com/scaraveos/keyval.js#readme

它用

  • 一个存储键的对象,它允许快速删除和值检索操作和
  • 一个允许真正快速值迭代的链表

希望能帮助到你 :)

为什么不添加一个jsperf链接?
2021-03-25 05:20:18
那个图书馆很棒,很棒的作品。只是为我节省了大量时间。
2021-03-28 05:20:18

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

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

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