如何在Swift中按属性值对自定义对象数组进行排序

650

假设我们有一个自定义类名为imageFile,这个类包含两个属性:

class imageFile  {
    var fileName = String()
    var fileID = Int()
}

很多数据存储在数组中:

var images : Array = []

var aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 101
images.append(aImage)

aImage = imageFile()
aImage.fileName = "image1.png"
aImage.fileID = 202
images.append(aImage)

如何按照“fileID”的升序或降序对图像数组进行排序?

20个回答

13
如果数组元素符合 Comparable,那么您可以简单地使用函数语法:
array.sort(by: <)

如果您基于自定义类型进行排序,您只需要实现 < 运算符:
class ImageFile {
    let fileName: String
    let fileID: Int
    let fileSize: Int
    static func < (left: ImageFile, right: ImageFile) -> Bool {
        return left.fileID < right.fileID
    }
}

然而,有时您不想使用一种标准的比较ImageFile的方式。也许在某些情况下,您希望基于fileID对图像进行排序,而在其他地方,您希望基于fileSize进行排序。对于动态比较,您有两个选项。

sorted(by:)

images = images.sorted(by: { a, b in
    // Return true if `a` belongs before `b` in the sorted array
    if a.fileID < b.fileID { return true }
    if a.fileID > b.fileID { return false }
    // Break ties by comparing file sizes
    return a.fileSize > b.fileSize
})

你可以使用尾随闭包简化语法:
images.sorted { ... }

手动输入if语句可能会导致代码过长(如果我们想通过文件名排序来打破文件大小的平局,我们将会有一个相当可怕的if链)。我们可以通过使用全新的SortComparator协议(macOS 12+,iOS 15+)避免这种语法:

sorted(using:)

files = files.sorted(using: [
    KeyPathComparator(\.fileID, order: .forward),
    KeyPathComparator(\.fileSize, order: .reverse),
])

这段代码根据文件ID进行排序(.forward表示升序),如果有相同的ID,则按照文件大小排序(.reverse表示降序)。\.fileID语法用于指定key paths。您可以根据需要扩展比较器列表。

5
这个答案是唯一一个突出 KeyPathComparator 强大之处的,应该被置顶。 - parapote

11

按照以下方式从fileID属性返回排序后的数组:

Swift 2

let sortedArray = images.sorted({ $0.fileID > $1.fileID })

Swift 3或4

let sortedArray = images.sorted(by: { $0.fileID > $1.fileID })

Swift 5.0

let sortedArray = images.sorted {
    $0.fileID < $1.fileID
}

9
如果您要在多个地方对该数组进行排序,那么将您的数组类型定义为Comparable可能是有意义的。
class MyImageType: Comparable, Printable {
    var fileID: Int

    // For Printable
    var description: String {
        get {
            return "ID: \(fileID)"
        }
    }

    init(fileID: Int) {
        self.fileID = fileID
    }
}

// For Comparable
func <(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID < right.fileID
}

// For Comparable
func ==(left: MyImageType, right: MyImageType) -> Bool {
    return left.fileID == right.fileID
}

let one = MyImageType(fileID: 1)
let two = MyImageType(fileID: 2)
let twoA = MyImageType(fileID: 2)
let three = MyImageType(fileID: 3)

let a1 = [one, three, two]

// return a sorted array
println(sorted(a1)) // "[ID: 1, ID: 2, ID: 3]"

var a2 = [two, one, twoA, three]

// sort the array 'in place'
sort(&a2)
println(a2) // "[ID: 1, ID: 2, ID: 2, ID: 3]"

7
如果您未使用自定义对象,而是使用实现Comparable协议的值类型(Int、String等),则可以简单地执行以下操作:
myArray.sort(>) //sort descending order

一个例子:
struct MyStruct: Comparable {
    var name = "Untitled"
}

func <(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name < rhs.name
}
// Implementation of == required by Equatable
func ==(lhs: MyStruct, rhs: MyStruct) -> Bool {
    return lhs.name == rhs.name
}

let value1 = MyStruct()
var value2 = MyStruct()

value2.name = "A New Name"

var anArray:[MyStruct] = []
anArray.append(value1)
anArray.append(value2)

anArray.sort(>) // This will sort the array in descending order

在 Swift 3 中,它是 myArray.sorted(by: >) - beryllium

6

Swift 3,4,5

    struct imageFile  {
        var fileName = String()
        var fileID = Int()
    }
    
    //append objects like this
    var arrImages = [imageFile]()
    arrImages.append(.init(fileName: "Hello1.png", fileID: 1))
    arrImages.append(.init(fileName: "Hello3.png", fileID: 3))
    arrImages.append(.init(fileName: "Hello2.png",fileID: 2))

    
    //array sorting using below code
    let sortImagesArr = arrImages.sorted(by: {$0.fileID < $1.fileID})
    print(sortImagesArr)
    
    //output
    
    imageFile(fileName: "Hello1.png", fileID: 1),
    imageFile(fileName: "Hello2.png", fileID: 2),
    imageFile(fileName: "Hello3.png", fileID: 3)

5
我这样做就可以了: var images = [imageFile]() images.sorted(by: {$0.fileID.compare($1.fileID) == .orderedAscending }) 这段代码的作用是将数组images按照fileID从小到大排序。

3
如果您想对自定义对象的原始数组进行排序,这里有另一种在Swift 2.1中实现的方法。
var myCustomerArray = [Customer]()
myCustomerArray.sortInPlace {(customer1:Customer, customer2:Customer) -> Bool in
    customer1.id < customer2.id
}

其中,id 是一个整数。对于字符串属性,您也可以使用相同的 < 运算符。

您可以通过查看此处的示例来了解其用法: Swift2: Nearby Customers


3
var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

students.sort(by: >)

print(students)

打印: "["彼得", "克鲁", "科菲", "阿科苏亚", "阿贝纳"]"


2

使用KeyPath进行排序

您可以像这样使用KeyPath进行排序:

myArray.sorted(by: \.fileName, <) /* using `<` for ascending sorting */

通过实施这个小而有用的扩展。
extension Collection{
    func sorted<Value: Comparable>(
        by keyPath: KeyPath<Element, Value>,
        _ comparator: (_ lhs: Value, _ rhs: Value) -> Bool) -> [Element] {
        sorted { comparator($0[keyPath: keyPath], $1[keyPath: keyPath]) }
    }
}

希望Swift在不久的将来将其添加到语言核心中。


1
这个问题已经在这里得到了回答 https://dev59.com/bKXja4cB1Zd3GeqPVr2h#46601105 ,同时也提供了可变方法。 - Leo Dabus
可变版本 public extension MutableCollection where Self: RandomAccessCollection { mutating func sort<T>(_ keyPath: KeyPath<Element, T>, by areInIncreasingOrder: (T, T) throws -> Bool) rethrows where T: Comparable { try sort { try areInIncreasingOrder($0[keyPath: keyPath], $1[keyPath: keyPath]) } }} - Leo Dabus

1

Swift 3 & 4 & 5

我遇到了一些与大小写有关的问题

所以我写了以下代码

let sortedImages = images.sorted(by: { $0.fileID.lowercased() < $1.fileID.lowercased() })

然后在此之后使用sortedImages。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接