Search code examples
seqnim-lang

Nim: Find index of element in seq based on predicate


If I have a sequence of values, how would I find the index of an element based on a predicate function? For example, if I had the following seq:

let values = @["pie", "cake", "ice cream"]

How would I find the index of the first element with four characters? I know of find, but it seems to only find index by equality, and does not allow passing a predicate. I could implement this myself but it feels as if it should be be in the standard library if find is.


Solution

  • A simple solution would be to use map from sequtils to map the predicate over the input sequence, and then to use find to get the index of the first true value in the result. This returns -1 when no element of the sequence satisfies the predicate:

    import sequtils
    
    proc isLen4(s: string): bool =
      len(s) == 4
    
    echo map(@["pie", "cake", "ice cream"], isLen4).find(true)  #--> 1
    

    This works, but is bad for large sequences since map processes the entire sequence. Thus even when the first element satisfies the predicate the entire sequence is processed. It would be better to just write a findIf procedure that returns the current index when the predicate is satisfied instead of continuing to process the rest of the input:

    proc findIf[T](s: seq[T], pred: proc(x: T): bool): int =
      result = -1  # return -1 if no items satisfy the predicate
      for i, x in s:
        if pred(x):
          result = i
          break
    
    echo @["pie", "cake", "ice cream"].findIf(isLen4)  #--> 1