如何获得几个 immutable.js 列表的联合

Posted

技术标签:

【中文标题】如何获得几个 immutable.js 列表的联合【英文标题】:How to get union of several immutable.js Lists 【发布时间】:2015-07-19 12:56:08 【问题描述】:

所以,我有一个列表:

let a = Immutable.List([1])

和列表 b:

let b = Immutable.List([2, 3])

我想从他们那里得到 List union === List([1, 2, 3])

我尝试merge他们的拳头:

let union = a.merge(b); // List([2, 3])

似乎merge 方法使用索引而不是值操作,因此用List b 的第一项覆盖List a 的第一项。所以,我的问题是获得多个列表并集的最简单方法是什么(最好不要迭代它们和其他额外操作)。

【问题讨论】:

您在寻找a.concat(b)吗? @loganfsmyth 上帝该死,不要在文档中注意到它)谢谢。您可以将其粘贴为答案,以便我接受。 Lmao,我也没有看到concat..尝试了各种东西..谢谢:) 我觉得你需要看看Set数据类型。 【参考方案1】:

你对合并是正确的。合并将使用合并列表的当前值更新索引。所以在你的情况下,你有

[0] = 1

并与

合并
[0] = 2
[1] = 3

最终用[0]=2 覆盖[0]=1,然后设置[1]=3 导致合并后观察到的[2,3] 数组。

解决这个问题的一个非常简单的方法是使用concat

var a = Immutable.List([1]);
var b = Immutable.List([2,3]); 

var c = a.concat(b);

它适用于这种情况。但是,如果情况更复杂,这可能是不正确的。例如,

var a = Immutable.List([1,4]);
var b = Immutable.List([2,3,4]); 

这会给你两个 4,这在技术上不再是一个并集。不幸的是,Immutable 中没有包含联合。实现它的一种简单方法是将每个列表中的每个值设置为对象的键,然后将这些键作为结果联合。

jsFiddle Demo

function union(left,right)
 //object to use for holding keys
 var union = ;

 //takes the first array and adds its values as keys to the union object
 left.forEach(function(x)
  union[x] = undefined;
 );

 //takes the second array and adds its values as keys to the union object
 right.forEach(function(x)
  union[x] = undefined;
 );

 //uses the keys of the union object in the constructor of List 
 //to return the same type we started with
 //parseInt is used in map to ensure the value type is retained
 //it would be string otherwise
 return Immutable.List(Object.keys(union).map(function(i) 
  return parseInt(i,10); 
 ));

这个进程是O(2(n+m))。任何使用containsindexOf 的进程最终都会成为O(n^2),这就是这里使用密钥的原因。

后期编辑

超高性能

function union(left,right)
    var list = [], screen = ;
    for(var i = 0; i < left.length; i++)
        if(!screen[left[i]])list.push(i);
        screen[left[i]] = 1;
    
    for(var i = 0; i < right.length; i++)
        if(!screen[right[i]])list.push(i);
        screen[right[i]] = 1;
    
    return Immutable.List(list);

【讨论】:

我相信这作为一种通用方法可能会出现问题 - 当值更复杂时。它适用于不可变类型的值 - 例如 Map - 因为该类型的 toString() 方法已实现 - 但这意味着每次将值添加为对象键时,您最终都会调用 toString() - 结果性能受到影响。 @DoronZavelevsky - 我不同意,作为一般方法,这没有任何问题。在您担心使用 toString 的特定微优化中,我制作了一个特殊版本,以避免在其他人也担心的情况下出现这种情况。您可以在后期编辑部分看到它。【参考方案2】:

实际上 Immutable.js 确实有一个联合 - 它用于 Set 数据结构:

https://facebook.github.io/immutable-js/docs/#/Set/union

Immutable.js 的伟大之处在于它有助于将更多的函数式编程结构引入到 JS 中——在这种情况下,一个通用接口和抽象数据类型的能力。因此,为了在您的列表上调用 union - 将它们转换为集合,使用 union 然后将它们转换回列表:

var a = Immutable.List([1, 4]);
var b = Immutable.List([2, 3, 4]); 
a.toSet().union(b.toSet()).toList(); //if you call toArray() or toJS() on this it will return [1, 4, 2, 3] which would be union and avoid the problem mentioned in Travis J's answer.

【讨论】:

感谢您的回答,但我想它是反模式而不是模式。与使用内置 concat 方法相比,将多个列表转换为集合然后返回似乎是开销。 我喜欢这个答案的简单性。相比之下,Travis J 的答案的表现让我感到惊讶:jsperf.com/immutable-union/2 是的,你不想经常这样做,老实说,如果使用集合操作,使用集合数据结构可能是有意义的。这就是说,我同意,函数式编程结构可以提供非常优雅和简单的解决方案——我很想知道使用 mori.js 而不是不可变的类似操作的性能(因为它是一个性能更高的库) 我尝试了该命令,虽然效果很好,但我确实得到了重复值(在我的情况下,列表由类实例(对象)组成,检查和删除同一键上的重复项的最佳方法是什么?我的对象的 ID?谢谢,肖恩。 注意:Set 不保留项目的传入顺序。这是一个很大的警告。【参考方案3】:

自发布此问题以来,List#merge 的实现发生了变化,在当前版本中 4.0.0-rc-12 List#merge 按预期工作并解决了问题。

【讨论】:

以上是关于如何获得几个 immutable.js 列表的联合的主要内容,如果未能解决你的问题,请参考以下文章

Immutable.js 与 Angular 2

TypeScript 的 `readonly` 可以完全替代 Immutable.js 吗?

如何定义一定大小的数组 immutable.js

在 TypeScript 中使用 Immutable JS 以正确的方式检索 List/Map 的值

如何使用Immutable.js在嵌套Map中添加新的键/值对

Immutable.js 以及在 react+redux 项目中的实践