Швидко, як сортувати масив користувацьких об'єктів за значенням властивості


520

Скажімо, у нас є власний клас з назвою 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)

питання: як я можу сортувати масив зображень за ASC або DESC?


сортувати по ключовому шляху stackoverflow.com/a/46601105/2303865
Лео Dabus

Відповіді:


940

Спочатку оголосіть свій масив як набраний масив, щоб ви могли викликати методи під час ітерації:

var images : [imageFile] = []

Тоді ви можете просто зробити:

Швидкий 2

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

Швидкий 3+

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

Наведений вище приклад дає порядок сортування desc


1
Я бракував частини декларації масиву, це зробило трюк Array <imageFile>.
mohacs

1
@AlexWayne У мене є NSManagedObjectпідклас під назвою CheckInAndOut . І в окремому файлі я оголосив набраний масив для об'єктів цього типу, і коли я намагаюся його сортувати , я отримую помилку користувача не можу знайти . Будь-яка ідея, чому це?
Ісуру

3
Я знайшов свою проблему. Мабуть, масив не був набраним масивом. У всякому разі, у мене є новий випуск. Як можна сортувати масив за кількома властивостями? Скажіть, у мене є 2 властивості, як firstNameі lastNameв масиві Personоб'єктів. Спершу я хочу сортувати його за firstNameпотім lastName. Як я можу це зробити?
Ісуру

12
вам тепер потрібно робити images.sortInPlace({ $0.fileID > $1.fileID })?
Тейлор М

13
на випадок, коли хтось цікавиться тим же: у відповіді дадуть наказ desc
Danny Wang

223

[ Оновлено для Swift 3 з сортуванням (за :) ] Це, використовуючи останнє закриття:

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

де ви використовуєте <або >залежно від ASC або DESC відповідно. Якщо ви хочете змінити 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

@MuneefM просто повертає string1.length <string2.length
Surjeet Rajput

sortбільше не компілюється з цим синтаксисом у Xcode 8. Xcode 8 каже, що $0.fileID < $1.fileIDстворює Bool not ComparisonResult.
Crashalot

3
Код цієї відповіді прекрасно працює в Xcode8; якщо у вас помилка, поставте нове запитання.
GoZoner

Чи можу я також використати це для сортування за допомогою порівнянь, наприклад, для сортування масиву за днями тижня? якщо так, як?
Крістофер

53

Майже кожен каже, як безпосередньо, дозвольте мені показати розвиток:

ви можете використовувати методи екземпляра Array:

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

Детальне пояснення щодо принципу роботи сортування див. У сортованій функції .


Чи можу я також використати це для сортування за допомогою порівнянь, наприклад, для сортування масиву за днями тижня? якщо так, як?
Крістофер

Дякуємо, що опублікували відповідь, яка показує, як працює закриття замість того, щоб читачі розуміли загадковий синтаксис "спрощеного" закриття!
користувач1118321

50

Швидкий 3

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

Я спробував це зі порівнянням дат, не зміг його працювати. Будь-яка ідея?
Ebru Güngör

Не NSDate або String, поточний швидкий 3 об'єкт дати.
Ebru Güngör

З якою властивістю Date ви порівнюєте? Властивість має бути в змозі порівнюватись із використовуваною функцією (більшою, ніж у моєму прикладі)
quemeful

9
Це єдина корисна відповідь на 2017 рік.
Fattie

@Fattie Що ти маєш на увазі? Правильний синтаксисpeople.sort { $0.email > $1.email }
Лев Дабус

43

З 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()для сортування елементів за порядком зростання. У наступному коді ігрової площадки показано, як користуватися 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:)якщо вам потрібно сортувати колекцію за місцем.



20

Ви також можете зробити щось подібне

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

тож ваш масив зображень буде зберігатися як упорядкований


19

Швидкий перехід від 2 до 4

Оригінальна відповідь прагнула сортувати масив користувацьких об’єктів за допомогою деякої властивості. Нижче я покажу вам декілька зручних способів зробити таку саму поведінку під швидкими структурами даних!

Невеликі речі не вийшли з ладу, я так незначно змінив ImageFile. Зважаючи на це, я створюю масив із трьох файлів зображень. Зауважте, що метадані - необов'язкове значення, очікується перехід у нуль як параметр.

 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 має властивість з назвою розмір. На наступних прикладах я покажу вам, як використовувати операції сортування з властивостями, такими як розмір.

від найменшого до найбільшого розміру (<)

    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
    }

Далі будемо сортувати за допомогою імені властивості String. Таким же чином використовуйте сортування для порівняння рядків. Але зауважте, внутрішній блок повертає результат порівняння. Цей результат визначатиме сортування.

AZ (. Упорядкований по зростанню)

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

ZA (. Упорядковано спадаючий)

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

Далі - мій улюблений спосіб сортування, у багатьох випадках один матиме необов’язкові властивості. Тепер не хвилюйтеся, ми будемо сортувати таким же чином, як вище, за винятком того, що нам доведеться обробляти нуль! У виробництві;

Я використовував цей код, щоб змусити останні екземпляри в моєму масиві з нульовими значеннями властивості бути останніми. Потім упорядкуйте метадані, використовуючи припущені розпаковані значення.

    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
    }

Можливий варіант вторинного сортування для необов'язкових. Наприклад; можна було показати зображення з метаданими та упорядкованими за розміром.


1
Як правило, відповіді набагато корисніші, якщо вони включають пояснення того, що призначений для виконання коду та чому це вирішує проблему, не вводячи інших.
Том Аранда

Набагато краще.
Том Аранда

18

Дві альтернативи

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
re 2) Який сенс побудувати порожній масив і відкинути його в наступному рядку? Я б запропонував використовувати var assignmentsO : [Assignment]або об'єднати його в один рядок, використовуючиlet assignmentsO = self.assignments.sort({ $0.order < $1.order })
Герман Клекер

2
Привіт Герман! Між написанням читабельного та ефективного коду існує дуже тонка грань. У цьому випадку єдиний момент - зробити його більш читабельним для громади;) насолоджуйтесь!
Бернауер

18

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 = imageFile()
   image.fileId = 13
   image.fileName = "B"

Тепер знову додайте об’єкт зображення до масиву arr

    arr.append(image)

Тепер ми застосуємо порядку зростання по властивості fileId в об'єктах arr array. Використовуйте <символ для порядку зростання

 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

Тепер ми застосуємо порядку зменшення до властивості fileId в об'єктах arr array. Використовувати > символ для порядку зменшення

 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
}

8

Якщо ви будете сортувати цей масив у декількох місцях, можливо, має сенс зробити тип масиву порівнянним.

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]"

6

Якщо ви не використовуєте власні об’єкти, а типи значень, натомість реалізує порівнянний протокол (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

у швидкому 3 цеmyArray.sorted(by: >)
берилій

6

Ви повертаєте відсортований масив із властивості fileID наступним чином:

Швидкий 2

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

Швидкий 3 АБО 4

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

Швидкий 5.0

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

Працює як шарм .. прихильне! (Пратік Праджапаті, Ахмедабад)
НСПратик

4

Я це роблю так, і це працює:

var images = [imageFile]() images.sorted(by: {$0.fileID.compare($1.fileID) == .orderedAscending })


2

Якщо ви хочете сортувати оригінальний масив користувацьких об'єктів. Ось ще один спосіб зробити це у Swift 2.1

var myCustomerArray = [Customer]()
myCustomerArray.sortInPlace {(customer1:Customer, customer2:Customer) -> Bool in
    customer1.id < customer2.id
}

Де idціле число. Ви можете використовувати той самий <оператор і для Stringвластивостей.

Ви можете дізнатися більше про його використання, переглянувши приклад тут: Swift2: Клієнти, які перебувають поблизу



1

Швидкий 3 та 4 та 5

У мене були деякі проблеми, пов'язані з малими літерами та великими справами

тому я зробив цей код

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

а потім використовувати sortedImages після цього


0

Сортувати за допомогою 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]) }
    }
}

Надія Свіфт додасть це найближчим часом до основної мови.


На це вже відповіли тут stackoverflow.com/a/46601105/2303865, а також метод мутації.
Лев Дабус

мутаційна версія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]) } }}
Лев Дабус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.