【问题标题】:Convert an array of IDs to a map object将 ID 数组转换为地图对象
【发布时间】:2017-12-06 21:42:54
【问题描述】:

我正在寻找一种简洁的方法来创建一个对象,该对象的属性名称与数组中的值匹配,其中所有属性都具有相同的值(例如true)。举个例子:

我的 ID 数组看起来像这些任意(在实际软件中,不一定是唯一的,也不一定是数字,而是原始的)值:

var ids = [4, 15, 8, 16, 42, 23];

我正在寻找一些简单的调用来将其转换为以下对象:

{ 4: true, 15: true, 8: true, 16: true, 42: true, 23: true }

由于 JavaScript 对象内部是哈希图,这样的对象应该更方便快速检查给定 ID 是否包含在数组中。


现在,当然我知道如何生成这个对象了。这是一个四线:

var idMap = {};
ids.forEach(function (v) {
    idMap[v] = true;
});

由于这种模式在我的代码中反复出现(在此应用程序中处理了许多不同的项目 ID),我觉得其中的“样板”百分比仍然太大。此外,在我看来,四行代码有时会分散对处理各种这些 ID 列表的实际棘手算法的注意力。因此,我正在寻找一种更短的方法来编写它,希望标准 JavaScript 或 lodash 有一些内置的东西,它已经在我们的应用程序中使用。

我能找到的最相关的问题似乎是lodash: mapping array to object。然而,它提出的解决方案并没有真正比上面更短和更易读,基本上用另一种样板代码替换了一种样板代码(在我的例子中,我通常会丢弃建议的解决方案用作对象属性的值和始终使用true 或一些类似的小真实值)。

是否有内置的东西允许使用非常简单的命令,例如

var idMap = arrayToMap(ids);

var idMap = arrayToMap(ids, true);

我还没找到谁的真名?

(显然,后备解决方案是在我们的应用程序中简单地编写这样一个函数。不过,我更愿意尽可能地坚持现有的函数,因此这个问题。)

【问题讨论】:

  • 为什么不直接使用 indexOf: var ids = [4, 15, 8, 16, 42, 23]; console.log(ids.indexOf(16)!=-1); ?然后将其删除为false或将其更改为x16
  • @mplungjan:因为indexOf has a time complexity of O(n)。在处理可能很容易有数千甚至一百万个元素的 ID 集时使用它是相当不明智的,并且会快速连续进行许多此类检查。相比之下,对象内部是哈希映射,访问对象属性应该是 O(1)。
  • 好的。我的错。在您的问题中没有实际提及一千或一百万个元素
  • 由于数组的大小,您担心 O(1) 与 O(n) 的性能,我想知道您是否考虑过使用 JavaScript 以外的其他技术来做到这一点?像数据库?
  • @James:也许,建议使用不同技术的解决方案仍然可以从 Web 应用程序的客户端部分离线使用,这也是一个有效的答案。

标签: javascript arrays lodash


【解决方案1】:

好吧,我认为您可以轻松地将源数组映射到对数组,然后轻松构造对象(甚至是ES6 Map)。

只要做:

_.fromPairs(ids.map(a=>[a,true])) 就是这样。

这是一个工作示例:

var ids = [4, 15, 8, 16, 42, 23];

var idsMap = _.fromPairs(ids.map(a=>[a,true])); // however true can be replaced with a variable

console.log(idsMap);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

另一种方式:

_(ids).keyBy(v=>v).mapValues(v=>true).value()

以下是工作示例:

var ids = [4, 15, 8, 16, 42, 23];

var idsMap = _(ids).keyBy(v=>v).mapValues(x=>true).value();

console.log(idsMap)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

而且,如果您真的确定(考虑)您的所有 id 都是真实值,那么您可以将其作为一个优势,只需创建一个具有相同键/值对的映射,并使用任何 id 直接用O(1)

只是做:

_.keyBy(ids) 你已经完成了:)

并且您不需要第二个解决方案中的额外.mapValues(v=>true),因为您知道值的真实性。

这里是工作的 sn-p:

var ids = [4, 15, 8, 16, 42, 23];

var idsMap = _.keyBy(ids);

console.log(idsMap);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

【讨论】:

  • 嗯,这基本上就是a now deleted answer 的建议。我承认您的版本要短得多,尽管创建可能数千个对数组只是为了匹配目标函数的签名似乎仍然是一种浪费的绕道。不过,第二个建议看起来很有趣。我会尝试了解它是如何工作的。
  • 好的,所以问题是,您的 id 是否将包含 0,如果您的 id 都不是虚假值,那么我有更好的方法来制作真实值的地图对象
  • 我想我可以排除任何虚假值是 ID。
  • 那么,您确定您在 ID 中的值将是真实值,对吧?
  • 是的,所有 ID 都是真实值。
【解决方案2】:

您可以创建一个为您生成地图的简单函数:

var ids = [4, 15, 8, 16, 42, 23];

function hash(arr, prop) {
  return arr.reduce(function(r, n) {
    r[prop ? n[prop] : n] = true;
    
    return r;
  }, {});
}

console.log(hash(ids));

var objs = [{ a: 1 }, { a: 2 }, { a: 3}];

console.log(hash(objs, 'a'));

你可以使用 ES6 Set:

const ids = [4, 15, 8, 16, 42, 23];

const set = new Set(ids);

console.log(set.has(16));

【讨论】:

  • 很有希望。我在此看到的唯一缺点是,在内部,这些值似乎被保存为对象(具有一个属性value,它再次包含该值)。这对我来说似乎有点浪费,因为我的代码很容易处理包含一百万个条目的 ID 集,这些条目受到 _.cloneDeep 的影响。
  • @O.R.Mapper - Set 只保存值,而不是保存一对prop: true。如果您使用 Set 的迭代器,您看到的 value: number 是迭代器将(懒惰地)产生的值。
  • @O.R.Mapper - 此外,如果您有十万个或更多条目,最好在服务器端处理它们,或者如果您无法在客户端提供类似 indexeddb 的帮助.
  • 啊,很高兴了解我在调试器中看到的值对象。至于在服务器端处理这些条目,我们在这方面受到了一些限制,因为这些 ID 紧密相连(有时从/转发到)完全在客户端(由第 3 方显示)的复杂视觉内容零件)。至于使用 indexeddb,我的理解是,这意味着半永久(跨会话/页面访问)存储(取决于环境,略有限制)数量的数据。我不确定这是高度临时性和...的适当解决方案...
  • ...快速改变对象。到目前为止,使用Set 似乎是最好的选择,假设检索以 O(1) 运行时复杂度运行。唯一可能的问题是,根据docs,MSIE 不支持new Set(iterable) 构造函数。
【解决方案3】:

您可以将其添加到应用程序中的Array.prototype

var ids = [4, 15, 8, 16, 42, 23];

Array.prototype.toThing = function(propVal) {
    return this.reduce((res, val) => {
        res[val] = propVal;
        return res;
    }, {})
}
console.log(ids.toThing(true));

【讨论】:

  • 我会对此表示赞同,因为它对于某些项目来说是一个可行的解决方案。如果这是 我的 项目,我会的,但在一个拥有 50 多个开发人员的大型开发项目中,在“随机”位置添加任何全局内容可能是撤销提交权限的好方法;)跨度>
  • 很公平,在这种情况下它不会真正起作用。祝你任务顺利。
猜你喜欢
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
  • 2018-12-01
  • 2017-07-30
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
  • 2015-04-15
相关资源
最近更新 更多