Permalink
Fetching contributors…
Cannot retrieve contributors at this time
74 lines (54 sloc) 2.75 KB

Add first(where:) method to Sequence

Introduction

Add a new extension method to Sequence called first(where:) that returns the found element.

Discussion on swift-evolution started with a proposal with title Add find method to SequenceType

Swift-evolution thread: Proposal: Add function SequenceType.find()

Review

Motivation

It's often useful to find the first element of a sequence that passes some given predicate. For Collections you can call index(of:) or index(where:) and pass the resulting index back into the subscript, but this is a bit awkward. For Sequences, there's no easy way to do this besides a manual loop that doesn't require filtering the entire sequence and producing an array.

I have seen people write code like seq.lazy.filter(predicate).first, but this doesn't actually work lazily because .first is only a method on Collection, which means the call to filter() ends up resolving to the Sequence.filter() that returns an Array instead of to LazySequenceProtocol.filter() that returns a lazy sequence. Users typically aren't aware of this, which means they end up doing a lot more work than expected.

Proposed solution

Extend Sequence with a method called first(where:) that takes a predicate and returns an optional value of the first element that passes the predicate, if any.

Detailed design

Add the following extension to Sequence:

extension Sequence {
  /// Returns the first element where `predicate` returns `true`, or `nil`
  /// if such value is not found.
  public func first(where predicate: @noescape (Self.Iterator.Element) throws -> Bool) rethrows -> Self.Iterator.Element? {
    for elt in self {
      if try predicate(elt) {
        return elt
      }
    }
    return nil
  }
}

Impact on existing code

None, this feature is purely additive.

In theory, we might provide an automatic conversion from seq.filter(predicate).first or seq.lazy.filter(predicate).first to seq.first(where: predicate), although the existing code would continue to compile just fine.

Alternatives considered

None