Extending Array to check if it is sorted in Swift?
In Swift 4.2 and later you can cobble together allSatisfy
and zip
with some sequence slicing:
extension Array where Element: Comparable {
func isAscending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(<=)
}
func isDescending() -> Bool {
return zip(self, self.dropFirst()).allSatisfy(>=)
}
}
In Swift 2.0 you can now extend protocols!
extension CollectionType where Generator.Element: Comparable {
public var isSorted: Bool {
var previousIndex = startIndex
var currentIndex = startIndex.successor()
while currentIndex != endIndex {
if self[previousIndex] > self[currentIndex] {
return false
}
previousIndex = currentIndex
currentIndex = currentIndex.successor()
}
return true
}
}
[1, 2, 3, 4].isSorted // true
["a", "b", "c", "e"].isSorted // true
["b", "a", "c", "e"].isSorted // false
[/* Anything not implementing `Comparable` */].isSorted // <~~ Type-error
Note that because we're using Indexable.Index
instead of a simple Int
as an index we have to use a while-loop instead, which looks a bit less pretty and clear.
The alternative solution to a free function is to do what Swift's built-in Array.sort
and Array.sorted
methods do, and require that you pass a suitable comparator to the method:
extension Array {
func isSorted(isOrderedBefore: (T, T) -> Bool) -> Bool {
for i in 1..<self.count {
if !isOrderedBefore(self[i-1], self[i]) {
return false
}
}
return true
}
}
[1, 5, 3].isSorted(<) // false
[1, 5, 10].isSorted(<) // true
[3.5, 2.1, -5.4].isSorted(>) // true
Actually, you can extend the Sequence
protocol for a more generic solution:
extension Sequence {
func isSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Bool {
var iterator = makeIterator()
guard var previous = iterator.next() else {
// Sequence is empty
return true
}
while let current = iterator.next() {
guard try areInIncreasingOrder(previous, current) else {
return false
}
previous = current
}
return true
}
}
extension Sequence where Element : Comparable {
func isSorted() -> Bool {
return isSorted(by: <)
}
}