How do I iterate over Dict entries safely without worrying about missing keys?

← Safe Value Access · Ref: Q167

EK9 Dicts provide three iteration approaches: for-in over entries, .keys() iterator, and .values() iterator. All are safe because every yielded item is guaranteed to be set.

FOR-IN OVER ENTRIES

The simplest and most common pattern:

  for entry in dict
    stdout.println(`${entry.key()}: ${entry.value()}`)

Each entry is a DictEntry with .key() and .value() accessors.

KEYS ITERATOR

Iterate over just the keys:

  keyIter <- dict.keys()
  while keyIter?
    stdout.println(keyIter.next())

VALUES ITERATOR

Iterate over just the values:

  valIter <- dict.values()
  while valIter?
    stdout.println($valIter.next())

STREAM PIPELINE

Use cat with a Dict for stream processing:

  cat dict | map with entryToString | collect as List of String

Streams over DictEntry items.

See Q46 for Dict basics. See Q90 for Dict operations including keys/values. See Q89 for stream pipelines.

See Q46 for dict. See Q166 for consistent safe pattern.

Example

defines module qa.safeaccess.dictiteration

  defines function

    entryToString() as pure
      -> entry as DictEntry of (String, Integer)
      <- rtn as String: `${entry.key()} = ${entry.value()}`

  defines program
    DictIterationDemo()
      stdout <- Stdout()

      scores <- {"Alice": 95, "Bob": 87, "Charlie": 92}

      // === FOR-IN OVER ENTRIES ===

      stdout.println("All entries:")
      for entry in scores
        stdout.println(`  ${entry.key()}: ${entry.value()}`)

      // === KEYS ITERATOR ===

      stdout.println("Keys:")
      keyIter <- scores.keys()
      while keyIter?
        stdout.println(`  ${keyIter.next()}`)

      // === VALUES ITERATOR ===

      stdout.println("Values:")
      valIter <- scores.values()
      while valIter?
        stdout.println(`  ${valIter.next()}`)

      // === STREAM PIPELINE ===

      formatted <- cat scores | map with entryToString | collect as List of String
      stdout.println(`Formatted: ${formatted}`)

      // === SAFE: EMPTY DICT ITERATION ===

      emptyDict <- Dict() of (String, Integer)
      for entry in emptyDict
        stdout.println("Should not print")
      stdout.println("Empty dict iteration completed safely")

Common mistakes

E06020 — DictEntry requires two type parameters (key and value types). Providing only one triggers E06020 — incorrect number of parameters supplied. Use DictEntry of (KeyType, ValueType). See ek9 -h E06020 for details.

Incorrect:

-> entry as DictEntry of String

Correct:

-> entry as DictEntry of (String, Integer)
Other ways to ask this
  • How do I loop over all key-value pairs in an EK9 Dict?
  • How do I use for-in with a Dict in EK9?
  • How do I iterate over Dict keys and values separately in EK9?

Coming from another language?

Java: map.entrySet().forEach(), map.keySet(), map.values(). Python: for k, v in dict.items(), dict.keys(), dict.values(). Rust: for (k, v) in map.iter(). Go: for k, v := range m. JavaScript: Object.entries(), Object.keys(), Object.values(). EK9: for entry in dict yields DictEntry, .keys()/.values() return iterators, cat dict for stream pipelines.

Keywords: null-safe, values, access, keys, dict, guard, loop, safe, DictEntry, stream, for, entries, iterate