Extending Array to check if it is sorted in Swift?

前端 未结 11 2403
清歌不尽
清歌不尽 2020-12-13 14:23

I want to extend Array class so that it can know whether it is sorted (ascending) or not. I want to add a computed property called isSorted. How can I state the

相关标签:
11条回答
  • 2020-12-13 15:07

    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.

    0 讨论(0)
  • 2020-12-13 15:07

    Here is a solution in Swift 4 that won't crash when self.count is equal or less than 1:

    extension Array where Element: Comparable {
        func isSorted(by isOrderedBefore: (Element, Element) -> Bool) -> Bool {
            for i in stride(from: 1, to: self.count, by: 1) {
                if !isOrderedBefore(self[i-1], self[i]) {
                    return false
                }
            }
            return true
        }
    }
    

    This snippet supposes that an array of 1 or 0 elements is already sorted.

    The reason to start with 1 in the for-loop range is: In case self.count <= 1, the loop will be skipped, a small performance increase. Using stride instead of ..< avoids the crash when the upper bound is < than the lower bound of a range.

    Here are some examples:

    [1, 2, 3].isSorted(by: >) // true
    [3, 2, 2].isSorted(by: >=) // true
    [1, 4, 7].isSorted(by: {x, y in
        return x + 2 < y * y
    }) // true
    
    let a: [Int] = [1]
    a.isSorted(by: <) // true
    
    
    let b: [Int] = []
    b.isSorted(by: >) // true
    
    0 讨论(0)
  • 2020-12-13 15:09

    Adaptation, a solution that will work in Swift 4

    extension Array where Iterator.Element: Comparable {
        func isSorted(isOrderedBefore: (Iterator.Element, Iterator.Element) -> Bool) -> Bool  {
            for i in 1 ..< self.count {
                if isOrderedBefore(self[i], self[i-1]) {
                    return false
                }
            }
            return true
        }
    }
    
    0 讨论(0)
  • 2020-12-13 15:09

    The most flexible solution to me is a combination of NSAddict's and Wes Campaigne's answer. I.e. combine the advantage of being able to extend protocols and to pass comparator functions as arguments. This eliminates the restrictions both to use it only with arrays and to constrain it to elements conforming to Comparable protocol.

    extension CollectionType
    {
        func isSorted(isOrderedBefore: (Generator.Element, Generator.Element) -> Bool) -> Bool
        {
            var previousIndex = startIndex
            var currentIndex = startIndex.successor()
    
            while currentIndex != endIndex
            {
                if isOrderedBefore(self[previousIndex], self[currentIndex]) == false
                {
                    return false
                }
    
                previousIndex = currentIndex
                currentIndex = currentIndex.successor()
            }
    
            return true
        }
    }
    

    This can be used on any Collection type and sorting criteria can be defined according to your needs.

    0 讨论(0)
  • 2020-12-13 15:11

    You've hit a problem with Swift's generics that can't be solved the way you like it right now (maybe in a future Swift version). See also Swift Generics issue.

    Currently, you'll need to define a function (for example at the global scope):

    func isSorted<T: Comparable>(array: Array<T>) -> Bool {
        for i in 1..<array.count {
            if array[i-1] > array[i] {
                return false
            }
        }
    
        return true
    }
    
    let i = [1, 2, 3]
    let j = [2, 1, 3]
    let k = [UIView(), UIView()]
    println(isSorted(i)) // Prints "true"
    println(isSorted(j)) // Prints "false"
    println(isSorted(k)) // Error: Missing argument for parameter #2 in call
    

    The error message is misleading, IMHO, as the actual error is something like "UIView doesn't satisfy the type constraint Comparable".

    0 讨论(0)
提交回复
热议问题