如何在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个回答

1131

首先,将您的数组声明为类型化数组,这样您就可以在迭代时调用方法:

var images : [imageFile] = []

那么你只需要简单地执行以下操作:

Swift 2

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

Swift 3

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

Swift 5

images.sorted { $0.fileId > $1.fileID }

上面的示例以降序给出结果。


如果有人发现这个答案有帮助的话:我是通过查阅Swift标准库参考文档独立找到这个答案的 - https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/SwiftStandardLibraryReference.pdf - Tommy
13
现在你是否需要执行images.sortInPlace({ $0.fileID > $1.fileID })操作? - Taylor M
我可以在同一行中添加第三个比较吗? - cmario
我也可以用这个来进行比较排序吗?例如,让数组按照一周中的日期排序?如果可以,怎么做? - Kristofer
@Kristofer 是的,您可以仅使用<运算符而无需定义$0和$1,因为它们是相同类型和已知类型。 - Rakshitha Muranga Rodrigo
显示剩余2条评论

282

[使用 sort(by:) 更新至 Swift 3] 使用一个尾随闭包:

images.sorted { $0.fileID < $1.fileID }

当你需要按升序或降序排序时,分别使用<>。如果想修改images数组,请使用以下代码:

images.sort { $0.fileID < $1.fileID }

如果你想要重复进行这个操作并且更喜欢定义一个函数的话,一种方法是:

func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool {
  return this.fileID < that.fileID
}

然后将其用作:

images.sort(by: sorterForFileIDASC)

我该如何将其与字符串一起使用?我需要按字符串长度对字符串进行排序。 - Muneef M
1
@MuneefM 只需返回 string1.length < string2.length - Surjeet Rajput
我能否使用它来进行比较排序,例如按星期几对数组进行排序?如果可以,如何实现? - Kristofer
这可能听起来很奇怪,但是...在这个答案中sorterForFileIDASC函数中给出的比较符号不正确吗?作者本人提到使用<进行升序排序,但是在sorterForFileIDASC中使用了>,据我所知,这应该是一个升序排序。我试图提出一个编辑建议,将符号翻转过来,但显然编辑必须至少有6个字符,所以我甚至无法提出建议。 - Cem Schemel
@CemSchemel,这取决于要比较的顺序,对吧? - Vega
是的,确实如此。但我认为示例函数试图实现升序排序(因此命名为sorterForFileIDASC),但实际比较会导致降序排序。我已经提出了修改意见并得到了接受,所以我认为这个问题在当前版本的答案中不再适用。 - Cem Schemel

64

Swift 3

people = people.sorted(by: { $0.email > $1.email })

我已经尝试使用日期比较,但无法使其工作。有什么想法吗? - Ebru Güngör
1
不是NSDate或String,而是当前的Swift 3日期对象。 - Ebru Güngör
你要比较哪个 Date 属性?该属性必须能够与所使用的函数进行比较(例如我的示例中使用的大于号)。 - quemeful

63

在Swift 5中,Array有两个方法称为sorted()sorted(by:)。第一个方法sorted()的声明如下:

返回已排序的集合元素。

func sorted() -> [Element]
第二种方法 sorted(by:) 的声明如下:

使用给定的谓词作为元素之间比较的方式,返回集合的元素按顺序排列。


返回集合的元素,使用给定的谓词作为元素之间的比较方式进行排序。

func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]

#1. 对可比对象进行升序排序

如果你的集合中的元素类型符合 Comparable 协议,你可以使用 sorted() 来对其进行升序排序。以下 Playground 代码展示了如何使用 sorted()

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted()
print(sortedImages)

/*
 prints: [ImageFile with ID: 100, ImageFile with ID: 200, ImageFile with ID: 300]
 */

#2. 对于可比较的对象降序排序

如果你的集合中元素类型符合Comparable协议,你需要使用sorted(by:)对元素进行降序排序。

class ImageFile: CustomStringConvertible, Comparable {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

    static func ==(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID == rhs.fileID
    }

    static func <(lhs: ImageFile, rhs: ImageFile) -> Bool {
        return lhs.fileID < rhs.fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0 > img1
})
//let sortedImages = images.sorted(by: >) // also works
//let sortedImages = images.sorted { $0 > $1 } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */

#3. 对于无法比较的对象进行升序或降序排序

如果你的集合中的元素类型不符合Comparable协议,那么你需要使用sorted(by:)方法对其进行升序或降序排序。

class ImageFile: CustomStringConvertible {

    let fileName: String
    let fileID: Int
    var description: String { return "ImageFile with ID: \(fileID)" }

    init(fileName: String, fileID: Int) {
        self.fileName = fileName
        self.fileID = fileID
    }

}

let images = [
    ImageFile(fileName: "Car", fileID: 300),
    ImageFile(fileName: "Boat", fileID: 100),
    ImageFile(fileName: "Plane", fileID: 200)
]

let sortedImages = images.sorted(by: { (img0: ImageFile, img1: ImageFile) -> Bool in
    return img0.fileID < img1.fileID
})
//let sortedImages = images.sorted { $0.fileID < $1.fileID } // also works
print(sortedImages)

/*
 prints: [ImageFile with ID: 300, ImageFile with ID: 200, ImageFile with ID: 100]
 */
请注意,Swift 还提供了两种方法叫做 sort()sort(by:),它们是 sorted()sorted(by:) 的原地排序版本。

如果您想按多个属性排序,可以传入一个元组($.lhs.fileID,$.lhs.fileName)<($.lhs.fileID,$.lhs.fileName)。您可以将排序与(lhs.val1,rhs.val2)>(rhs.val1,lhs.val2)混合使用。最后,如果您想要具有不同于升序和降序的自定义排序(就像我希望标题始终出现在内容之前一样),则可以为_static func>_提供不同的重载。 - green_knight

59

几乎每个人都直接给出“如何”,让我展示一下演变过程:

您可以使用数组的实例方法:

// general form of closure
images.sortInPlace({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })

// types of closure's parameters and return value can be inferred by Swift, so they are omitted along with the return arrow (->)
images.sortInPlace({ image1, image2 in return image1.fileID > image2.fileID })

// Single-expression closures can implicitly return the result of their single expression by omitting the "return" keyword
images.sortInPlace({ image1, image2 in image1.fileID > image2.fileID })

// closure's argument list along with "in" keyword can be omitted, $0, $1, $2, and so on are used to refer the closure's first, second, third arguments and so on
images.sortInPlace({ $0.fileID > $1.fileID })

// the simplification of the closure is the same
images = images.sort({ (image1: imageFile, image2: imageFile) -> Bool in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in return image1.fileID > image2.fileID })
images = images.sort({ image1, image2 in image1.fileID > image2.fileID })
images = images.sort({ $0.fileID > $1.fileID })

有关排序工作原理的详细解释,请参见排序函数


28

在 Swift 3.0 中

images.sort(by: { (first: imageFile, second: imageFile) -> Bool in
    first. fileID < second. fileID
})

22

Swift 4.0, 4.1 & 4.2:

首先,我创建了一个可变数组,类型为imageFile,如下所示:

var arr = [imageFile]()
创建可变对象类型为imageFile的图片,并按以下方式为属性赋值
var image = imageFile()
image.fileId = 14
image.fileName = "A"

现在,将这个对象追加到数组arr

arr.append(image)

现在,将不同属性分配给同一个可变对象,即 image

image = imageFile()
image.fileId = 13
image.fileName = "B"

现在,再次将图像对象附加到数组 arr

arr.append(image)

现在,我们将对数组 arr 中的 fileId 属性应用升序排列。使用<符号进行升序排列。

arr = arr.sorted(by: {$0.fileId < $1.fileId}) // arr has all objects in Ascending order
print("sorted array is",arr[0].fileId)// sorted array is 13
print("sorted array is",arr[1].fileId)//sorted array is 14

现在,我们将对数组arr对象中的fileId属性应用降序排序。使用>符号表示降序排序。

arr = arr.sorted(by: {$0.fileId > $1.fileId}) // arr has all objects in Descending order
print("Unsorted array is",arr[0].fileId)// Unsorted array is 14
print("Unsorted array is",arr[1].fileId)// Unsorted array is 13

在Swift 4.1和4.2中,要使用排序顺序,请使用

let sortedArr = arr.sorted { (id1, id2) -> Bool in
  return id1.fileId < id2.fileId // Use > for Descending order
}

22

你也可以做类似这样的事情

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

所以你的图像数组将以排序方式存储


20

Swift 2到4版本

原始问题需要使用某些属性对自定义对象的数组进行排序。下面我将向您展示一些方便的方法,使用Swift数据结构实现相同的行为!

稍微提一下,我略微改动了ImageFile。有了这个想法,我创建了一个包含三个图像文件的数组。请注意,metadata是一个可选值,传入nil作为参数是可以接受的。

 struct ImageFile {
      var name: String
      var metadata: String?
      var size: Int
    }

    var images: [ImageFile] = [ImageFile(name: "HelloWorld", metadata: nil, size: 256), ImageFile(name: "Traveling Salesmen", metadata: "uh this is huge", size: 1024), ImageFile(name: "Slack", metadata: "what's in this stuff?", size: 2048) ]

ImageFile有一个名为size的属性。下面的例子将展示如何使用排序操作来操作像size这样的属性。

按大小从小到大排序(<)

    let sizeSmallestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size < next.size
    }

从大到小排序 (>)

    let sizeBiggestSorted = images.sorted { (initial, next) -> Bool in
      return initial.size > next.size
    }

接下来,我们将使用字符串属性名称进行排序。同样地,使用 sort 比较字符串。但请注意,内部块返回一个比较结果。该结果将定义排序。

按字母顺序升序排列(.orderedAscending)

    let nameAscendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedAscending
    }

Z-A (.orderedDescending)

    let nameDescendingSorted = images.sorted { (initial, next) -> Bool in
      return initial.name.compare(next.name) == .orderedDescending
    }

接下来是我最喜欢的排序方式,在许多情况下,我们会有可选的属性。现在不用担心,我们要像上面一样进行排序,只是我们要处理空值!实际应用中;

我使用这段代码将数组中所有具有 nil 属性值的实例强制放到最后。然后,使用假定非空值对元数据进行排序。

    let metadataFirst = images.sorted { (initial, next) -> Bool in
      guard initial.metadata != nil else { return true }
      guard next.metadata != nil else { return true }
      return initial.metadata!.compare(next.metadata!) == .orderedAscending
    }

可对可选项进行第二排序。例如,可以显示带有元数据并按大小排序的图像。


19

两种选择

1)使用sortInPlace对原数组进行排序。

self.assignments.sortInPlace({ $0.order < $1.order })
self.printAssignments(assignments)

2) 使用另一个数组存储有序数组

var assignmentsO = [Assignment] ()
assignmentsO = self.assignments.sort({ $0.order < $1.order })
self.printAssignments(assignmentsO)

3
在下一行中构建一个空数组并将其丢弃的意义何在?我建议使用 var assignmentsO : [Assignment] 或将其合并为一行,使用 let assignmentsO = self.assignments.sort({ $0.order < $1.order }) - Hermann Klecker
2
嗨,赫尔曼!编写易读且高效的代码之间有一条非常微弱的界限。在这种情况下,唯一的重点是使其更易读给社区使用;)享受吧! - Bernauer

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