7

I just saw a short video from Seth Ladd on Collections.

A Set has only unique elements (are not ordered), but sometimes i need an ordered list and i want to remove all duplicates (the 2nd occurrence of an element e.g. String should be removed from the list)

original input to a list: A, B, C, B, D, A should result in A, B, C, D. I need to keep the order. A result like B, A, D, C would not help me.

8
  • 1
    Why is there no such function? Because the Dart team made the same decision to not provide one, like the designers of many other standard libraries before them. Do you want such a function? Then you'll either have to implement it yourself, or find a library to do it for you. What is the goal of this question? To find out why this isn't part of the standard library, or to find out how to do it regardless of that fact? Commented Feb 2, 2013 at 20:05
  • All those operations on a list would be O(n^2) naively. To get better bounds, either sorting or a Set would be used internally. This should be easy to create a helper function for this (if one doesn't exist) using Sets and a probe. (As for why these aren't defined on a list itself - not constructive.) Commented Feb 2, 2013 at 20:05
  • Possible duplicate of How to delete duplicates in a dart List? list.distinct()? Commented Feb 2, 2013 at 20:07
  • (Note how removing the irrelevant question and altering the title made this question "Constructive". It's also a duplicate, but perhaps now there will be less down-votes.) Commented Feb 2, 2013 at 20:12
  • I would like to know what the reason for the lack of that function is. Commented Feb 2, 2013 at 20:27

4 Answers 4

11

Use toSet and then toList

  var ids2 = ["A", "B", "C", "B", "D", "A"];
  var result = ids2.toSet().toList();

[A, B, C, D]
Sign up to request clarification or add additional context in comments.

1 Comment

and what if my list has instances of a class? like [Instance of Foo], [Instance of Foo], [Instance of Foo]? Is it possible to remove the duplicates while preserving the object instances?
3

Justin Fagnani already gave a good answer. Here is another one:

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.
}

1 Comment

note: LinkedHashMap is now in dart:collection, and we are probably going to add an InsertionOrderedSet soon.
2

It's fairly easy to implement on your own:

Iterable distinct(Iterable i) {
  var set = new Set();
  return i.where((e) {
    var isNew = !set.contains(e);
    set.add(e);
    return isNew;
  });

It'd be even nicer if Set.add() returned a bool that indicated whether the set was modified:

Iterable distinct(Iterable i) {
  var set = new Set();
  return i.where((e) => set.add(e));
}

You can file feature request bugs of course.

Edit: As Florian points out, the above solution only works if the returned Iterable is only used once. Subsequent uses will return Iterators with no elements, because even element has been seen already on the first use.

To solve this we need to keep a visited set for every Iterator created from the returned Iterable, not just one for the Iterable. We can do that by creating Iterable and Iterator subclasses like with 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;
}

Yes, this is much longer, but it'll work correctly with many-use finite Iterables and one-use infinite Iterables. The infinite iterable use case could easily have problems with memory, which is an argument for not including it in the core lib and forcing developers to make some decisions about what exactly they need.

5 Comments

thx for the answer. I think i needed distinct() like 1 year ago and i wrote my own removeDuplicates(). I thought they need some time to implement the "official" dart-distinct(). And now I hear it is still not available. I just wonder: why?
There are a lot of things that aren't there yet. Did you file a feature request?
The solution works, but only, if the iterator is used only once.
Accidentally pressed <enter> and then spent too much time before updating the comment... The reason it only works once, is, that the returned iterable will redo the 'where' every time it is used. But the 'set' will not be reset. One easy solution is to force the evaluation of the filter with ".toList()".
Tricky, @FlorianLoitsch, thanks for pointing out the problem. I was thinking that this would preserve the laziness but I see the issue. This is why these utilities should be in the core lib :) I'll try to add a correct version.
1

Using generics and generators, you can make a function that works with Iterables of any type

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);
  }
}

Usage of distinct(["A", "B", "C", "B", "D", "A"])

Or if you'd like to wrap it into an extension:

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);
    }
  }
}

Usage of ["A", "B", "C", "B", "D", "A"].distinct()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.