_(Swift 3)_
### Check if an element exists in an array (fulfilling some criteria), _and if so, proceed working with the first such element_
If the intent is:
1. To check whether an element exist in an array (/fulfils some boolean criteria, not necessarily equality testing),
2. And if so, proceed and work with the first such element,
Then an alternative to [`contains(_:)`](
[To see links please register here]
) as blueprinted `Sequence` is to [`first(where:)`](
[To see links please register here]
) of `Sequence`:
let elements = [1, 2, 3, 4, 5]
if let firstSuchElement = elements.first(where: { $0 == 4 }) {
print(firstSuchElement) // 4
// ...
}
In this contrived example, its usage might seem silly, but it's very useful if querying arrays of non-fundamental element types for existence of any elements fulfilling some condition. E.g.
struct Person {
let age: Int
let name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
let persons = [Person(17, "Fred"), Person(16, "Susan"),
Person(19, "Hannah"), Person(18, "Sarah"),
Person(23, "Sam"), Person(18, "Jane")]
if let eligableDriver = persons.first(where: { $0.age >= 18 }) {
print("\(eligableDriver.name) can possibly drive the rental car in Sweden.")
// ...
} // Hannah can possibly drive the rental car in Sweden.
let daniel = Person(18, "Daniel")
if let sameAgeAsDaniel = persons.first(where: { $0.age == daniel.age }) {
print("\(sameAgeAsDaniel.name) is the same age as \(daniel.name).")
// ...
} // Sarah is the same age as Daniel.
Any chained operations using `.filter { ... some condition }.first` can favourably be replaced with `first(where:)`. The latter shows intent better, and have performance advantages over possible non-lazy appliances of `.filter`, as these will pass the full array prior to extracting the (possible) first element passing the filter.
---
### Check if an element exists in an array (fulfilling some criteria), _and if so, remove the first such element_
A comment below queries:
> How can I remove the `firstSuchElement` from the array?
A similar use case to the one above is to _remove_ the first element that fulfils a given predicate. To do so, the [`index(where:)`](
[To see links please register here]
) method of [`Collection`](
[To see links please register here]
) (which is readily available to array collection) may be used to find the index of the first element fulfilling the predicate, whereafter the index can be used with the [`remove(at:)`](
[To see links please register here]
) method of `Array` to (possible; given that it exists) remove that element.
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let indexOfFirstSuchElement = elements.index(where: { $0 == "c" }) {
elements.remove(at: indexOfFirstSuchElement)
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
}
Or, if you'd like to remove the element from the array _and work with_, apply `Optional`:s [`map(_:)`](
[To see links please register here]
) method to conditionally (for `.some(...)` return from `index(where:)`) use the result from `index(where:)` to remove and capture the removed element from the array (within an optional binding clause).
var elements = ["a", "b", "c", "d", "e", "a", "b", "c"]
if let firstSuchElement = elements.index(where: { $0 == "c" })
.map({ elements.remove(at: $0) }) {
// if we enter here, the first such element have now been
// remove from the array
print(elements) // ["a", "b", "d", "e", "a", "b", "c"]
// and we may work with it
print(firstSuchElement) // c
}
Note that in the contrived example above the array members are simple value types (`String` instances), so using a predicate to find a given member is somewhat over-kill, as we might simply test for equality using the simpler `index(of:)` method as shown in [@DogCoffee's answer](
[To see links please register here]
). If applying the find-and-remove approach above to the `Person` example, however, using `index(where:)` with a predicate is appropriate (since we no longer test for equality but for fulfilling a supplied predicate).