Skip to content

Commit 1d8edfa

Browse files
author
Russ Bishop
committed
SR-1519: Add Sequence.first(predicate:)
1 parent 3018eeb commit 1d8edfa

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

stdlib/private/StdlibCollectionUnittest/CheckSequenceType.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ public struct FindTest {
127127
) {
128128
self.expected = expected
129129
self.element = MinimalEquatableValue(element)
130-
self.sequence = sequence.map(MinimalEquatableValue.init)
130+
var elementIndex = 0
131+
self.sequence = sequence.map {
132+
defer { elementIndex += 1 }
133+
return MinimalEquatableValue($0, identity: elementIndex)
134+
}
131135
self.expectedLeftoverSequence = expectedLeftoverSequence.map(
132136
MinimalEquatableValue.init)
133137
self.loc = SourceLoc(file, line, comment: "test data")

stdlib/public/core/Sequence.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,18 @@ public protocol Sequence {
564564
isSeparator: @noescape (Iterator.Element) throws -> Bool
565565
) rethrows -> [SubSequence]
566566

567+
/// Returns the first element of the sequence that satisfies the given
568+
/// predicate or nil if no such element is found.
569+
///
570+
/// - Parameter predicate: A closure that takes an element of the
571+
/// sequence as its argument and returns a Boolean value indicating
572+
/// whether the element is a match.
573+
/// - Returns: The first match or `nil` if there was no match.
574+
@warn_unused_result
575+
func first(
576+
predicate: @noescape (Iterator.Element) throws -> Bool
577+
) rethrows -> Iterator.Element?
578+
567579
@warn_unused_result
568580
func _customContainsEquatableElement(
569581
_ element: Iterator.Element
@@ -950,13 +962,32 @@ extension Sequence {
950962
///
951963
/// - Parameter body: A closure that takes an element of the sequence as a
952964
/// parameter.
965+
@warn_unused_result
953966
public func forEach(
954967
_ body: @noescape (Iterator.Element) throws -> Void
955968
) rethrows {
956969
for element in self {
957970
try body(element)
958971
}
959972
}
973+
974+
/// Returns the first element of the sequence that satisfies the given
975+
/// predicate or nil if no such element is found.
976+
///
977+
/// - Parameter predicate: A closure that takes an element of the
978+
/// sequence as its argument and returns a Boolean value indicating
979+
/// whether the element is a match.
980+
/// - Returns: The first match or `nil` if there was no match.
981+
public func first(
982+
predicate: @noescape (Iterator.Element) throws -> Bool
983+
) rethrows -> Iterator.Element? {
984+
for element in self {
985+
if try predicate(element) {
986+
return element
987+
}
988+
}
989+
return nil
990+
}
960991
}
961992

962993
extension Sequence where Iterator.Element : Equatable {

validation-test/stdlib/SequenceType.swift.gyb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,26 @@ SequenceTypeTests.test("contains/Predicate") {
616616
}
617617
}
618618

619+
//===----------------------------------------------------------------------===//
620+
// first()
621+
//===----------------------------------------------------------------------===//
622+
623+
SequenceTypeTests.test("first/Predicate") {
624+
for test in findTests {
625+
let s = MinimalSequence<MinimalEquatableValue>(elements: test.sequence)
626+
let found = s.first { $0 == test.element }
627+
expectEqual(
628+
test.expected == nil ? nil : test.element,
629+
found,
630+
stackTrace: SourceLocStack().with(test.loc))
631+
if let found = found {
632+
expectEqual(
633+
test.expected, found.identity,
634+
"find() should find only the first element matching its predicate")
635+
}
636+
}
637+
}
638+
619639
//===----------------------------------------------------------------------===//
620640
// reduce()
621641
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)