Find first element matching condition in Swift array (e.g. EKSource)
Solution 1
There's a version of indexOf
that takes a predicate closure - use it to find the index of the first local source (if it exists), and then use that index on eventStore.sources
:
if let index = eventStore.sources.indexOf({ $0.sourceType == .Local }) {
let eventSourceForLocal = eventStore.sources[index]
}
Alternately, you could add a generic find
method via an extension on SequenceType
:
extension SequenceType {
func find(@noescape predicate: (Self.Generator.Element) throws -> Bool) rethrows -> Self.Generator.Element? {
for element in self {
if try predicate(element) {
return element
}
}
return nil
}
}
let eventSourceForLocal = eventStore.sources.find({ $0.sourceType == .Local })
(Why isn't this there already?)
Solution 2
Alternatively in Swift3 you could use:
let local = eventStore.sources.first(where: {$0.sourceType == .Local})
Solution 3
I don't understand why you're using map
at all. Why not use filter
? You will then end up with all the local sources, but in actual fact there will probably be only one, or none, and you can readily find out by asking for the first one (it will be nil
if there isn't one):
let local = eventStore.sources.filter{$0.sourceType == .Local}.first
Solution 4
Swift 4 solution that also handles the situation when there are no elements in your array that match your condition:
if let firstMatch = yourArray.first{$0.id == lookupId} {
print("found it: \(firstMatch)")
} else {
print("nothing found :(")
}
Solution 5
Swift 5 If you want to find out from Array of Model then speciyfy $0.keyTofound otherwise use $0
if let index = listArray.firstIndex(where: { $0.id == lookupId }) {
print("Found at \(index)")
} else {
print("Not found")
}
Related videos on Youtube
Drux
Updated on July 09, 2022Comments
-
Drux almost 2 years
I would like to find the first
EKSource
of typeEKSourceType.Local
with a "single"-line expression in Swift. Here is what I currently have:let eventSourceForLocal = eventStore.sources[eventStore.sources.map({ $0.sourceType }) .indexOf(EKSourceType.Local)!]
Is there a better way of doing this (such as without mapping and/or with a generic version of
find
)? -
user3441734 over 8 yearsi am not sure why, but maybe because "SequenceType makes no requirement on conforming types regarding whether they will be destructively "consumed" by iteration. To ensure non-destructive iteration, constrain your sequence to CollectionType."
-
user3441734 over 8 yearsalso there is missing try part (predicate can throw)
-
Nate Cook over 8 years@user3441734: Good catch on the
try
, fixed. All the sequence operations will consume a sequence (map, contains, etc), so I don't think that's it. indexOf works fine but is limited to collections. -
Daniel Rinser about 8 yearsWhile this is the most straight-forward solution, it has the disadvantage of always filtering the whole collection, even though it only needs the first matching element. I'm not advocating for premature optimization, but for large collections that might matter.
-
matt about 8 years@DanielRinser I agree, I like Nate Cook's answer better.
-
Christopher Swasey about 8 yearsIMO premature optimization doesn't even come into play here—avoiding completely unnecessary work is neither premature nor optimization.
-
Daniel Galasko almost 8 yearsI called mine
findFirst
because thats a bit more self documenting:) -
Nate Cook almost 8 years@DanielGalasko Good news, Swift 3 now includes a
first(where:)
method on sequences that does just this. -
Daniel Galasko almost 8 yearsthats great @NateCook !
-
Daniel Galasko almost 8 yearsLink to proposal github.com/apple/swift-evolution/blob/master/proposals/…
-
Jeroen Leenarts over 7 yearsSo you make it
let local = eventStore.sources.lazy.filter{$0.sourceType == .Local}.first
-
Nick Gaens over 6 yearsIn Swift4, that becomes:
let local = eventStore.sources.first {$0.sourceType == .Local}
-
Joe about 5 yearsThis is the correct answer, using
filter
thenfirst
is sub optimal because it forces worst case ofn
time complexity. That means that even after you find the element, filter will still loop through the rest of the array.first(where:{})
short circuits traversal and breaks after it finds the first occurrence.