扩展数组以检查它是否在Swift中排序?

时间:2014-07-07 03:17:51

标签: arrays swift

我想扩展Array类,以便它可以知道它是否已排序(升序)。我想添加一个名为isSorted的计算属性。如何说明Array的元素可以比较?

我目前在Playground中的实现

extension Array {
  var isSorted: Bool {
    for i in 1..self.count {
      if self[i-1] > self[i] { return false }
    }
    return true
  }
}

// The way I want to get the computed property
[1, 1, 2, 3, 4, 5, 6, 7, 8].isSorted //= true
[2, 1, 3, 8, 5, 6, 7, 4, 8].isSorted //= false

错误 Could not find an overload for '>' that accepts the supplied arguments

当然,我仍然遇到错误,因为Swift不知道如何比较这些元素。如何在Swift中实现此扩展?或者我在这里做错了什么?

12 个答案:

答案 0 :(得分:28)

自由函数的替代解决方案是执行Swift的内置Array.sortArray.sorted方法,并要求您将合适的比较器传递给方法:

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

答案 1 :(得分:21)

在Swift 2.0中,您现在可以扩展协议!

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

请注意,因为我们使用Indexable.Index而不是简单的Int作为索引,所以我们必须使用while循环,这看起来不那么漂亮和清晰。

答案 2 :(得分:7)

你已经遇到了Swift的泛型问题,这些问题无法按照你现在喜欢的方式解决(可能是未来的Swift版本)。另请参阅Swift Generics issue

目前,您需要定义一个函数(例如在全局范围内):

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

错误信息具有误导性,恕我直言,因为实际错误类似于&#34; UIView不满足类型约束Comparable&#34;。

答案 3 :(得分:4)

实际上,您可以扩展dice_coef协议以获得更通用的解决方案:

Sequence

答案 4 :(得分:2)

 适应,solution将在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
    }
}

答案 5 :(得分:1)

对我来说,最灵活的解决方案是结合使用NSAddict和Wes Campaigne的答案。即结合了能够扩展协议和将比较器函数作为参数传递的优点。这消除了仅将其用于数组并将其约束为符合Comparable协议的元素的限制。

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
    }
}

这可以在任何Collection类型上使用,并且可以根据您的需要定义排序标准。

答案 6 :(得分:1)

通用函数zip()可以为实现提供快捷方式。

extension Collection where Element: Comparable {
    var isSorted: Bool {
        guard count > 1 else {
            return true 
        }

        let pairs = zip(prefix(count - 1), suffix(count - 1))

        return !pairs.contains { previous, next in
            previous > next
        }
    }
}

[0, 1, 1, 2].isSorted  // true
[0, 2, 2, 1].isSorted  // false

答案 7 :(得分:0)

这是Swift 4中的一种解决方案,当self.count等于或小于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
    }
}

此代码段假定已经对1或0个元素的数组进行了排序。

在for循环范围内以1开头的原因是:如果self.count <= 1,则将跳过循环,性能会有所提高。使用stride代替..<可以避免当上限小于范围的下限时发生崩溃。

以下是一些示例:

[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

答案 8 :(得分:0)

如果您想要不带参数的简单函数,例如Swift中的sort()或sorted():

extension Array where Element : Comparable {
    func isSorted() -> Bool {
        guard self.count > 1 else {
            return true
        }

        for i in 1..<self.count {
            if self[i-1] > self[i] {
                return false
            }
        }
        return true
    }
}

答案 9 :(得分:0)

在Swift 4.2及更高版本中,您可以使用一些序列切片将allSatisfyzip拼凑在一起:

extension Array where Element: Comparable{
    func isAscending() -> Bool {
        return zip(self, self.dropFirst()).allSatisfy(<=)
    }

    func isDescending() -> Bool {
        return zip(self, self.dropFirst()).allSatisfy(>=)
    }
}

答案 10 :(得分:0)

@kAzec的答案似乎在元素相等时不起作用。这是因为根据the documentation,areInIncreasingOrder(a,a)必须为false。

以下内容应该可以正常工作。

extension Sequence {
    func isSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool)
        rethrows -> Bool {
        var it = makeIterator()
        guard var previous = it.next() else { return true }
        
        while let current = it.next() {
            if try !areInIncreasingOrder(previous, current) &&
                areInIncreasingOrder(current, previous) {
                return false
            }
            previous = current
        }
        return true
    }
}

extension Sequence where Element: Comparable {
    func isSorted() -> Bool {
        return isSorted(by: <)
    }
}

答案 11 :(得分:0)

总结以前的答案,有一个最小的通用数组扩展需要检查:

extension Array {
    func isSorted(_ predicate: (Element, Element) throws -> Bool) -> Bool {
        guard count > 1 else { return true }
        return (try? zip(self, self.dropFirst()).allSatisfy(predicate)) == true
    }
}

// Standard types

[1, 2, 3, 4, 5].isSorted(<) // true
[1, 2, 10, 4, 5].isSorted(<) // false

[10, 5, 4, 3, 1].isSorted(>) // true
[10, 20, 4, 3, 1].isSorted(>) // false


// Custom types

struct MyStruct {
    let i: Int
}

let items = [MyStruct(i: 1), MyStruct(i: 2), MyStruct(i: 3), MyStruct(i: 10)]
let sorted = items.isSorted { $0.i < $1.i } // true