How do I find the first item matching a condition in EK9?

← Common Collection Tasks · Ref: Q184

EK9 uses stream pipelines with filter and head 1 to find the first matching item.

FILTER + HEAD 1

The standard pattern for finding the first match:

  results <- cat items | filter by predicate | head 1 | collect as List of Integer

Returns a list with 0 or 1 elements. Check if empty to determine whether a match was found.

CHECK RESULT

  if ~results is empty
    found <- results.getOrDefault(0, defaultVal)
    stdout.println(`Found: ${found}`)

WHY HEAD 1

head 1 stops processing after the first match is found. This is EK9's equivalent of early exit: the stream terminates as soon as one element passes through.

See Q89 for stream pipelines. See Q125 for head/tail/skip. See Q162 for safe list access. See Q185 for any/all match.

Example

defines module qa.collectiontasks.findfirst

  defines function

    isEven() as pure
      -> num as Integer
      <- rtn as Boolean: num mod 2 == 0

    isLong() as pure
      -> str as String
      <- rtn <- Boolean()
      expectedValue <- 4
      rtn: length str > expectedValue

  defines program
    FindFirstMatchDemo()
      stdout <- Stdout()

      // === FILTER + HEAD 1 ===

      numbers <- [1, 3, 5, 4, 6, 8]
      firstEven <- cat numbers | filter by isEven | head 1 | collect as List of Integer
      stdout.println(`First even: ${firstEven}`)

      // === CHECK IF FOUND ===

      if ~firstEven is empty
        found <- firstEven.getOrDefault(0, 0)
        stdout.println(`Found: ${found}`)

      // === NO MATCH FOUND ===

      allOdd <- [1, 3, 5, 7, 9]
      noMatch <- cat allOdd | filter by isEven | head 1 | collect as List of Integer
      stdout.println(`No match result: ${noMatch}`)

      if noMatch is empty
        stdout.println("No even number found")

      // === FIND FIRST IN STRINGS ===

      names <- ["Bob", "Jo", "Alice", "Charlie"]
      longNames <- cat names | filter by isLong | head 1 | collect as List of String
      stdout.println(`First long name: ${longNames}`)

      // === ALL MATCHES (WITHOUT HEAD) ===

      allEvens <- cat numbers | filter by isEven | collect as List of Integer
      stdout.println(`All evens: ${allEvens}`)

Common mistakes

E07520 — A predicate function used with 'filter by' must return Boolean. Returning String instead of Boolean triggers E07520 because the stream filter needs a true/false decision. See ek9 -h E07520 for details.

Incorrect:

<- rtn as String: $num

Correct:

<- rtn as Boolean: num mod 2 == 0
Other ways to ask this
  • What is the EK9 equivalent of find() or findFirst()?
  • How do I search a list for the first match in EK9?
  • How do I filter a list and take the first result in EK9?

Coming from another language?

Java: list.stream().filter(pred).findFirst() returns Optional. Python: next((x for x in items if pred(x)), default). Rust: iter.find(pred) returns Option. Go: manual loop with break. JavaScript: arr.find(pred). Kotlin: list.find(pred), list.firstOrNull(pred). EK9: cat list | filter by pred | head 1 | collect as List, no break needed.

Keywords: find, search, first, stream, head, filter, predicate, match, task, condition, pipeline, collection