如何在保留顺序的同时删除列表中的重复元素?
Posted
技术标签:
【中文标题】如何在保留顺序的同时删除列表中的重复元素?【英文标题】:How to remove duplicate elements in a list while preserving order? 【发布时间】:2013-01-17 22:10:15 【问题描述】:我刚刚看到a short video from Seth Ladd on Collections
。
一个集合只有唯一的元素(没有排序),但有时我需要一个有序列表,我想删除所有重复项(第二次出现的元素,例如字符串应该从列表中删除)
列表的原始输入:A, B, C, B, D, A
应导致 A, B, C, D
。我需要保持秩序。 B, A, D, C
这样的结果对我没有帮助。
【问题讨论】:
为什么没有这个功能?因为 Dart 团队做出了不提供的相同决定,就像他们之前的许多其他标准库的设计者一样。你想要这样的功能吗?然后你要么必须自己实现它,要么找一个库来为你做。这个问题的目标是什么?找出为什么这不是标准库的一部分,或者找出如何不管这个事实如何去做? 列表上的所有这些操作都是O(n^2)
天真。为了获得更好的界限,内部使用排序或集合。这应该很容易使用 Sets 和探针为此创建一个辅助函数(如果不存在)。 (至于为什么这些没有在列表本身上定义 - 没有建设性。)
How to delete duplicates in a dart List? list.distinct()?的可能重复
(请注意如何删除不相关的问题并更改标题使这个问题“具有建设性”。它也是重复的,但现在可能会有更少的反对票。)
我想知道缺少那个功能的原因是什么。
【参考方案1】:
使用toSet
,然后使用toList
var ids2 = ["A", "B", "C", "B", "D", "A"];
var result = ids2.toSet().toList();
[A, B, C, D]
【讨论】:
如果我的列表有一个类的实例怎么办?喜欢[Instance of Foo], [Instance of Foo], [Instance of Foo]
?是否可以在保留对象实例的同时删除重复项?【参考方案2】:
Justin Fagnani 已经给出了很好的答案。这是另一个:
Iterable distinct(Iterable i)
var map = new LinkedHashMap();
i.forEach((x) map[x] = true; );
return map.keys; // map.keys.toList() would free the map for GC.
【讨论】:
注意:LinkedHashMap 现在在 dart:collection 中,我们可能很快会添加一个 InsertionOrderedSet。【参考方案3】:自行实现相当容易:
Iterable distinct(Iterable i)
var set = new Set();
return i.where((e)
var isNew = !set.contains(e);
set.add(e);
return isNew;
);
如果Set.add()
返回一个表明该集合是否被修改的布尔值,那就更好了:
Iterable distinct(Iterable i)
var set = new Set();
return i.where((e) => set.add(e));
您当然可以提交功能请求错误。
编辑:正如 Florian 指出的那样,上述解决方案仅在返回的 Iterable
仅使用一次时才有效。后续使用将返回没有元素的 Iterator
s,因为在第一次使用时已经看到偶数元素。
为了解决这个问题,我们需要为从返回的Iterable
创建的每个Iterator
保留一个访问集,而不仅仅是为Iterable
创建一个访问集。我们可以通过创建Iterable
和Iterator
子类来做到这一点,例如WhereIterable
/WhereIterator
:
Iterable distinct(Iterable i) => new DistinctIterable(i);
class DistinctIterable<E> extends Iterable<E>
final Iterable<E> _iterable;
DistinctIterable(this._iterable);
Iterator<E> get iterator
return new DistinctIterator<E>(_iterable.iterator);
class DistinctIterator<E> extends Iterator<E>
final Iterator<E> _iterator;
final Set<E> _visited = new Set<E>();
DistinctIterator(this._iterator);
bool moveNext()
while (_iterator.moveNext())
if (!_visited.contains(_iterator.current))
_visited.add(_iterator.current);
return true;
return false;
E get current => _iterator.current;
是的,这要长得多,但它可以与多次使用有限 Iterable
s 和一次性无限 Iterable
s 一起正常工作。无限可迭代用例很容易出现内存问题,这是不将其包含在核心库中的一个论据,并迫使开发人员就他们到底需要什么做出一些决定。
【讨论】:
感谢您的回答。我想我像 1 年前一样需要 distinct(),并且我编写了自己的 removeDuplicates()。我认为他们需要一些时间来实现“官方” dart-distinct()。现在我听说它仍然不可用。我只是想知道:为什么? 还有很多东西还没有。您是否提交了功能请求? 解决方案有效,但前提是迭代器仅使用一次。 不小心按了使用泛型和生成器,您可以创建一个适用于任何类型的 Iterables 的函数
Iterable<T> distinct<T>(Iterable<T> elements) sync*
final visited = <T>;
for (final el in elements)
if (visited.contains(el)) continue;
yield el;
visited.add(el);
distinct(["A", "B", "C", "B", "D", "A"])
的用法
或者,如果您想将其包装到扩展中:
extension IterableDistinctExt<T> on Iterable<T>
Iterable<T> distinct() sync*
final visited = <T>;
for (final el in this)
if (visited.contains(el)) continue;
yield el;
visited.add(el);
["A", "B", "C", "B", "D", "A"].distinct()
的用法
【讨论】:
以上是关于如何在保留顺序的同时删除列表中的重复元素?的主要内容,如果未能解决你的问题,请参考以下文章