Skip to content

Instantly share code, notes, and snippets.

@jnutting
Last active January 31, 2019 14:45
Show Gist options
  • Save jnutting/5a384fced8785727194c04cffc5b3b96 to your computer and use it in GitHub Desktop.
Save jnutting/5a384fced8785727194c04cffc5b3b96 to your computer and use it in GitHub Desktop.
A short-circuiting compactMap for Swift's Sequence, that returns up to a maximum number of non-nil items
/*
* Sequence's compactMap lets you transform one
* sequence of values into another, and strip
* out any nil results. Sometimes, all you want
* is the first non-nil result, and don't want
* to futher evaluate the transform for the
* remaining values in the original sequence.
* This lets you do that.
*/
extension Sequence {
func compactMap<ElementOfResult>(first maximum: UInt, _ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] {
var results = [ElementOfResult]()
for element in self {
if results.count >= maximum { break }
if let result = try transform(element) {
results.append(result)
}
}
return results
}
func compactMapFirst<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> ElementOfResult? {
return try compactMap(first: 1, transform).first
}
}
// Usage example:
let items = [1, 3, 7, 10, 15, 20, 21, 22, 23, 30, 34]
let transformTensMinusOne: (Int) -> Int? = { return $0 % 10 == 0 ? $0 - 1 : nil }
let tensMinusOne = items.compactMap(transformTensMinusOne) // -> [9, 19, 29]
let tensMinusOne_firstTwoArray = items.compactMap(first: 2, transformTensMinusOne) // -> [9, 19]
let tensMinusOne_firstOneArray = items.compactMap(first: 1, transformTensMinusOne) // -> [9]
let tensMinusOne_firstOne = items.compactMapFirst(transformTensMinusOne) // -> Optional(9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment