How do I use a Set for unique values in EK9?

← Collections and Data Structures · Ref: Q1365

Set of type T is a built-in collection of UNIQUE values. Adding a value that is already present has no effect, so it is the natural tool for de-duplication and fast membership testing.

INSERTION-ORDERED

Unlike a hash set in some languages, an EK9 Set is INSERTION-ORDERED (backed by a linked hash set, just like Dict). Iterating a Set yields values in the order they were first added, which is deterministic and reproducible across runs and JVMs - so output and tests stay stable. Do not rely on any other order.

CREATING AND ADDING

  seen <- Set() of String
  seen += "apple"
  seen += "banana"
  seen += "apple"      // duplicate - no effect

Uniqueness is by VALUE, using the element type's == and #? (hashcode) operators - the same mechanism that makes a value a valid Dict key.

MEMBERSHIP, SIZE, ITERATION

  found <- seen contains "banana"
  count <- seen.length()               // reflects the unique count
  for item in seen                     // first-insertion order
    stdout.println(item)

A Set is always set when created; an empty Set is a valid value, not unset.

UNION AND DIFFERENCE

  combined <- setA + setB              // union
  removed  <- setA - setB              // difference

These also have in-place forms (+=, -=) and merge (:~:).

WHY NOT DICT OF (T, BOOLEAN)?

Before Set, membership was faked with Dict of (T, Boolean). Set says what you mean, iterates in insertion order, and its length is the unique count directly.

List and Dict are the other core collections. Use 'ek9 -h Set' for the full API.

Example

defines module qa.set.unique

  defines program

    SetUnique()
      stdout <- Stdout()

      //A Set holds unique values; a duplicate add has no effect.
      seen <- Set() of String
      seen += "apple"
      seen += "banana"
      seen += "apple"

      count <- seen.length()
      stdout.println(`unique count: ${count}`)

      found <- seen contains "banana"
      stdout.println(`contains banana: ${found}`)

      //Iteration is in first-insertion order (deterministic).
      for item in seen
        stdout.println(item)

      //Union with another Set.
      other <- Set() of String
      other += "cherry"
      other += "date"
      combined <- seen + other
      stdout.println(`union count: ${combined.length()}`)

Common mistakes

E05030 — Set (like the other built-in generic collections) is CLOSED - it cannot be extended (E05030: not open to be extended). Extending a collection mixes collection mechanics with application logic. Use composition/delegation instead: hold a 'Set of String' as a field and expose the operations you need.

Incorrect:

MyThing extends Set of String

Correct:

MyThing
  items as Set of String
Other ways to ask this
  • How do I remove duplicates in EK9?
  • Is there a Set type in EK9?
  • How do I test membership efficiently?
  • How do I get the union or difference of two collections?

Coming from another language?

Java: HashSet (unordered) / LinkedHashSet (insertion order). Python: set (unordered) - EK9 differs by being insertion-ordered. Go: map[T]struct{} idiom. Rust: HashSet / IndexSet. EK9: Set of type T, insertion-ordered by design (like Dict), value-based uniqueness via == and #?, with +/-/+=/-=/contains/length and for-in iteration.

Keywords: set, dedup, distinct, order, duplicate, membership, collection, hashset, union, unique, insertion, difference, contains