Skip to content

Instantly share code, notes, and snippets.

@Revolucent
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Revolucent/1a8085746b950d91a0a8 to your computer and use it in GitHub Desktop.
Save Revolucent/1a8085746b950d91a0a8 to your computer and use it in GitHub Desktop.
Minimum/maximum implementations for SequenceType and Arrays in Swift
/*
Replace the name of my Swift module, Prosumma, with your own, or this will not compile.
As of 1.0, Swift lacks mixins (as in Ruby) or the ability to extend protocols (as in C#), which means lots of
cumbersome boilerplate. I've tried to cut down on that with this little snippet. First, you'll notice that
there are no actual minimum and maximum functions defined. To achieve that, do the following:
var lowest: Int? = [5, 4, 3].most(<)
var highest: Int? = [5, 4, 3].most(>)
Because of certain limitations of Swift's type system, type inference will not work here, so you have to say Int?
explicitly or it will not work.
Since it is not possible to create an extension on SequenceType directly, I created two global functions, both called
most. The first one allows the implementer to make a member of the sequence comparable by using a closure. Let me illustrate
what that means. Let's take MapKit's MKRoute class, which has a distance property. We want to compare MKRoutes by
their distance in order to find the shortest route. We could of course make MKRoute implement Comparable, but instead
we decide to use the second parameter of most as follows:
var shortestRoute: MKRoute? = most(routes, { $0.distance }, <)
This compares routes by their distance and returns the route (NOT the distance) with the shortest distance. If routes is empty,
most returns nil.
The second version of most is simpler. It can only be used with sequences whose elements are comparable, e.g.
var numbers = [2, 7, 4, 3]
var lowest: Int? = most(numbers, <)
The rest is pretty straightforward: a protocol called Most which contains object-oriented versions of the most functions,
with the first parameter implicit. Then I provided an implementation for Array using an extension. The methods in Array
forward to the global functions, allowing the following:
var shortestRoute: MKRoute? = routes.most({ $0.distance }, <)
var lowest: Int? = numbers.most(<)
Thanks to the global functions, Most can be added to any SequenceType fairly quickly.
*/
public func most<S: SequenceType, C: Comparable>(sequence: S, comparable: S.Generator.Element -> C, compare: (C, C) -> Bool) -> S.Generator.Element? {
var est: (S.Generator.Element, C)? = nil
for elem in sequence {
if est == nil {
est = (elem, comparable(elem))
} else {
var comparison = comparable(elem)
if compare(comparison, est!.1) {
est = (elem, comparison)
}
}
}
return est == nil ? nil : est!.0
}
public func most<C: Comparable, S: SequenceType where S.Generator.Element == C>(sequence: S, compare: (C, C) -> Bool) -> C? {
var est: C? = nil
for elem in sequence {
if est == nil {
est = elem
} else if compare(elem, est!) {
est = elem
}
}
return est
}
public protocol Most : SequenceType {
func most<C: Comparable>(comparable: Self.Generator.Element -> C, _ compare: (C, C) -> Bool) -> Self.Generator.Element?
func most<C: Comparable>(compare: (C, C) -> Bool) -> C?
}
extension Array : Most {
public func most<C: Comparable>(comparable: T -> C, _ compare: (C, C) -> Bool) -> T? {
return Prosumma.most(self, comparable, compare)
}
public func most<C: Comparable>(compare: (C, C) -> Bool) -> C? {
return Prosumma.most(map({ $0 as C }), compare)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment