【问题标题】:Objects vs arrays in Javascript for key/value pairsJavascript中的对象与数组的键/值对
【发布时间】:2010-10-15 20:15:03
【问题描述】:

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

(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。

【问题讨论】:

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

标签: javascript data-structures


【解决方案1】:

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

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

它使用

  • 一个存储键的对象,它允许快速删除和值检索 运营和
  • 一个允许非常快速的值迭代的链表

希望对你有帮助:)

【讨论】:

  • 为什么不添加一个jsperf链接?
  • 那个图书馆很棒,很棒。只是为我节省了很多时间。
【解决方案2】:

其实还有第四种选择:

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

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

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

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

来自 nickf 的编辑:我已经更改了 OP 中的示例,所以这个答案可能不再有意义。道歉。

后期编辑

好的,后期编辑,我会选择选项 (3)。它是可扩展的(易于添加新属性),具有快速查找的特点,并且还可以迭代。如果需要,它还允许您从条目返回 ID。

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

如果您 (a) 需要保留顺序,则选项 (2) 很好; (b) 需要迭代所有元素; (c) 不需要按 id 查找元素,除非按 id 排序(可以在 O(logn) 中进行二分查找。注意,当然,如果需要保留它已排序,然后您将支付插入费用。

【讨论】:

  • 我理解为什么 (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];'访问,它非常适用于任何类内部的快速内部数据存储——好吧,我的意思是“新WhatFunction();" Javascripters 不喜欢的东西,我喜欢 ;-)...
  • ...但这仅在 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); 这不是很好!)。
【解决方案3】:

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

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

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

【讨论】:

    【解决方案4】:

    考虑到您只能将 name 作为值,我会选择第一个选项。它是最干净的,开销最少,查找速度最快。

    【讨论】:

      【解决方案5】:

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

      结构的简单性意味着它的解析速度最快,并且在像这样的小型、很少(或从不)更改的数据集的情况下,我只能想象它会被频繁地执行——在这种情况下最小开销是要走的路。

      【讨论】:

        【解决方案6】:

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

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

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

        • 它是自我描述的,所以你不需要 必须依赖任何人使用 people 知道关键是用户的 id。
        • 每个对象都是独立的, 哪个更适合传递数据 其他地方 - 而不是两个参数 (身份证和姓名)你只是路过 人。
        • 这是一个罕见的问题,但有时 键值可能无效 用作键。例如,我曾经 想要映射字符串转换 (例如,“:”到“>”),但由于“:” 不是我必须的有效变量名 使用第二种方法。
        • 它很容易扩展,以防万一 沿线某处你需要 向部分(或全部)用户添加更多数据。 (对不起,我知道你的“为 争论的缘故”,但这是一个 重要方面。)

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

        【讨论】:

        • 关于第三点。可以用括号表示法访问对象属性以避免该问题: var x = { "a.b>c++" : "foo"};警报(x['a.b>c++']);
        • 你确实有一些保留字的问题,我相信。但是使用数字时没有问题。
        • @derobert:我不这么认为。 x = {“删除”:1,“为”:2,“功能”:3}; -- 这是有效的,可以用同样的方式访问。
        猜你喜欢
        • 2015-03-31
        • 2017-06-05
        • 1970-01-01
        • 2017-08-25
        • 1970-01-01
        • 2022-06-16
        • 2014-07-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多