Swift, как сортировать массив пользовательских объектов по значению свойства


521

Допустим, у нас есть собственный класс с именем 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' ASC или DESC?


сортировка по KeyPath stackoverflow.com/a/46601105/2303865
Лео Дабус

Ответы:


941

Сначала объявите ваш массив как типизированный массив, чтобы вы могли вызывать методы при выполнении итерации:

var images : [imageFile] = []

Тогда вы можете просто сделать:

Swift 2

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

Свифт 3+

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

Пример выше дает порядок сортировки desc


1
Мне не хватало части объявления массива, она сделала трюк Array <imageFile>.
mohacs

1
@AlexWayne У меня есть NSManagedObjectподкласс CheckInAndOut . И в отдельном файле я объявил типизированный массив для объектов этого типа, и когда я пытаюсь отсортировать его, я получаю сообщение об ошибке Could not find member . Есть идеи, почему это так?
Исуру,

3
Я нашел свою проблему. Очевидно, массив не был типизированным массивом. Во всяком случае, у меня есть новая проблема. Как я могу отсортировать массив по нескольким свойствам? Скажем, у меня есть 2 свойства, как firstNameи lastNameв массиве Personобъектов. Сначала я хочу отсортировать его, firstNameа затем lastName. Как я могу это сделать?
Исуру

12
тебе сейчас нужно сделать images.sortInPlace({ $0.fileID > $1.fileID })?
Тейлор М

13
в случае, если кому-то интересно то же самое: ответ даст порядок
Дэнни Ван

223

[ Обновлено для Swift 3 с сортировкой (by :) ] Это, используя замыкающее замыкание:

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, а не 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 })

Подробное описание принципа работы сортировки см. В разделе «Сортированная функция» .


Могу ли я также использовать это для сортировки по сравнению, например, с сортировкой массива по дням недели? если так как?
Кристофер

Спасибо за публикацию ответа, показывающего, как работает замыкание, вместо того, чтобы предполагать, что читатели понимают загадочный синтаксис «упрощенного» замыкания!
user1118321

50

Свифт 3

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

Я попробовал это с датой сравнения, не мог заставить его работать. Есть идеи?
Эбру Гюнгер,

Не NSDate или String, текущий объект Swift 3 Date.
Эбру Гюнгер,

Какое свойство даты вы сравниваете? Свойство должно быть в состоянии сравнивать с используемой функцией (больше, чем в моем примере)
quemeful

9
Это единственный полезный ответ на 2017 год.
Толстяк

@ Толстяк Что ты имеешь в виду? Правильный синтаксис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()для сортировки элементов в порядке возрастания. Следующий код 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:)если вам нужно отсортировать вашу коллекцию на месте.



20

Вы также можете сделать что-то вроде

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

поэтому ваш массив изображений будет сохранен как отсортированный


19

Свифт с 2 по 4

Первоначальный ответ пытался отсортировать массив пользовательских объектов с использованием некоторого свойства. Ниже я покажу вам несколько удобных способов сделать то же самое поведение с быстрыми структурами данных!

По мелочам я немного изменил ImageFile. Имея это в виду, я создаю массив с тремя файлами изображений. Обратите внимание, что метаданные являются необязательным значением, в качестве параметра ожидается передача значения 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. В следующих примерах я покажу вам, как использовать операции сортировки с такими свойствами, как размер.

от меньшего к большему размеру (<)

    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 (.orded Ascending)

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

ZA (.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
    }

Для дополнительных опций возможна вторичная сортировка. Например; можно показать изображения с метаданными и упорядочить по размеру.


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

2
Привет, Герман! Существует очень тонкая грань между написанием читаемого и эффективного кода. В этом случае единственный момент - сделать его более читабельным для сообщества;) наслаждайтесь!
Бернауэр

18

Swift 4.0, 4.1 и 4.2 Сначала я создал изменяемый массив типа imageFile (), как показано ниже

var arr = [imageFile]()

Создайте изображение изменяемого объекта типа imageFile () и присвойте значение свойствам, как показано ниже

   var image = imageFile()
   image.fileId = 14
   image.fileName = "A"

Теперь добавьте этот объект в массив

    arr.append(image)

Теперь присвойте разные свойства одному и тому же изменяемому объекту, т.е. изображению

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

Теперь снова добавьте изображение объекта в массив обр

    arr.append(image)

Теперь, мы будем применять порядок по возрастанию по 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

Теперь мы применим порядок по убыванию для свойства fileId в массиве arr. Используйте символ> для сортировки по убыванию

 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

Если вы используете не пользовательские объекты, а типы значений, которые реализуют 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

в быстром 3 этоmyArray.sorted(by: >)
бериллий

6

Вы возвращаете отсортированный массив из свойства fileID следующим образом:

Swift 2

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

Свифт 3 ИЛИ 4

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

Swift 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.