Swift como classificar matriz de objetos personalizados por valor da propriedade

521

digamos que temos uma classe personalizada chamada imageFile e essa classe contém duas propriedades.

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

muitos deles armazenados na matriz

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)

pergunta é: como posso classificar a matriz de imagens por 'fileID' ASC ou DESC?

mohacs
fonte
ordenar por KeyPath stackoverflow.com/a/46601105/2303865
Leo Dabus

Respostas:

941

Primeiro, declare sua matriz como uma matriz digitada para que você possa chamar métodos ao iterar:

var images : [imageFile] = []

Então você pode simplesmente fazer:

Swift 2

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

Swift 3+

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

O exemplo acima fornece a ordem de classificação desc

Alex Wayne
fonte
1
Estava faltando a parte da declaração da matriz, ele fez o truque Matriz <imageFile>.
Mohacs
1
@AlexWayne Eu tenho uma NSManagedObjectsubclasse chamada CheckInAndOut . E em um arquivo separado, declarei uma matriz digitada para objetos desse tipo e quando tento classificá- la, recebo um erro Não foi possível encontrar o membro . Alguma ideia de por que é assim?
Isuru 28/09
3
Eu encontrei o meu problema. Aparentemente, a matriz não era uma matriz digitada. De qualquer forma, tenho um novo problema. Como posso classificar uma matriz por várias propriedades? Digamos que eu tenha 2 propriedades como firstNamee lastNameem uma matriz de Personobjetos. Primeiro eu quero classificá-lo firstNamee depois lastName. Como eu posso fazer isso?
Isuru
12
agora você precisa fazer images.sortInPlace({ $0.fileID > $1.fileID })?
Taylor M
13
no caso de alguém está se perguntando o mesmo: a resposta vai dar fim desc
Danny Wang
223

[ Atualizado para Swift 3 com classificação (por :) ] Isso, explorando um fechamento à direita:

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

onde você usa <ou >depende de ASC ou DESC, respectivamente. Se você deseja modificar a imagesmatriz , use o seguinte:

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

Se você fizer isso repetidamente e preferir definir uma função, uma maneira é:

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

e depois use como:

images.sort(by: sorterForFileIDASC)
GoZoner
fonte
como posso processar isso com string? eu preciso corda ordenar por seu comprimento
Muneef M
@MuneefM apenas retorna string1.length <string2.length #
Surjeet Rajput
sortnão compila mais com essa sintaxe no Xcode 8. O Xcode 8 diz que $0.fileID < $1.fileIDproduz um Bool, não ComparisonResult.
Crashalot
3
O código desta resposta funciona bem no Xcode8; se você tiver um erro, poste uma nova pergunta.
GoZoner
Também posso usar isso para classificar por comparações, por exemplo, ordenando a matriz pelos dias da semana? Se sim, como?
Kristofer
53

Quase todo mundo dá o quão diretamente, deixe-me mostrar a evolução:

você pode usar os métodos de instância da matriz:

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

Para uma explicação elaborada sobre o princípio de funcionamento da classificação, consulte A função classificada .

fujianjin6471
fonte
Também posso usar isso para classificar por comparações, por exemplo, ordenando a matriz pelos dias da semana? Se sim, como?
Kristofer
Obrigado por postar uma resposta que mostre como um fechamento funciona em vez de assumir que os leitores entendem a sintaxe enigmática do fechamento "simplificado"!
user1118321
50

Swift 3

people = people.sorted(by: { $0.email > $1.email })
quemeful
fonte
Eu tentei isso com uma comparação de datas, não consegui fazê-lo funcionar. Qualquer ideia?
Ebru Güngör
Não é NSDate ou String, atual objeto de data rápido 3.
Ebru Güngör
Qual propriedade de Date você está comparando? A propriedade deve ser capaz de ser comparada com a função utilizada (maior do que no meu exemplo)
quemeful
9
Esta é a única resposta útil para 2017 em diante.
Fattie
@Fattie O que você quer dizer? A sintaxe correta épeople.sort { $0.email > $1.email }
Leo Dabus
43

Com o Swift 5, Arraytem dois métodos chamados sorted()e sorted(by:). O primeiro método,, sorted()tem a seguinte declaração:

Retorna os elementos da coleção, classificados.

func sorted() -> [Element]

O segundo método,, sorted(by:)tem a seguinte declaração:

Retorna os elementos da coleção, classificados usando o predicado fornecido como a comparação entre os elementos.

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

# 1 Classificar em ordem crescente para objetos comparáveis

Se o tipo de elemento dentro de sua coleção Comparableestiver em conformidade com o protocolo, você poderá usá-lo sorted()para classificar seus elementos em ordem crescente. O seguinte código do Playground mostra como usar 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 Classificar em ordem decrescente para objetos comparáveis

Se o tipo de elemento dentro de sua coleção Comparableestiver em conformidade com o protocolo, você precisará usar sorted(by:)para classificar seus elementos com uma ordem decrescente.

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 Classificar em ordem crescente ou decrescente para objetos não comparáveis

Se o tipo de elemento dentro de sua coleção NÃO Comparableestiver em conformidade com o protocolo, você precisará usar sorted(by:)para classificar seus elementos em ordem crescente ou decrescente.

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]
 */

Observe que o Swift também fornece dois métodos chamados sort()e sort(by:)como equivalentes sorted()e sorted(by:)se você precisar classificar sua coleção no local.

Imanou Petit
fonte
25

No Swift 3.0

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

Você também pode fazer algo como

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

para que sua matriz de imagens seja armazenada como classificada

Nicolas Grenié
fonte
19

Swift 2 a 4

A resposta original procurou classificar uma matriz de objetos personalizados usando alguma propriedade. Abaixo, mostrarei algumas maneiras úteis de fazer esse mesmo comportamento com estruturas de dados rápidas!

Pequenas coisas fora do caminho, eu mudei o ImageFile levemente. Com isso em mente, crio uma matriz com três arquivos de imagem. Observe que os metadados são um valor opcional, passando nulo como parâmetro.

 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 possui uma propriedade chamada size. Para os exemplos a seguir, mostrarei como usar operações de classificação com propriedades como tamanho.

do menor para o maior tamanho (<)

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

maior para o menor (>)

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

Em seguida, classificaremos usando o nome da propriedade String. Da mesma maneira, use sort para comparar strings. Mas observe que o bloco interno retorna um resultado de comparação. Este resultado definirá a classificação.

AZ (.orderedAscending)

    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
    }

A seguir, é minha maneira favorita de classificar; em muitos casos, um terá propriedades opcionais. Agora não se preocupe, vamos classificar da mesma maneira que acima, exceto que precisamos lidar com nada! Em produção;

Eu usei esse código para forçar todas as instâncias da minha matriz com valores de propriedade nulos a serem os últimos. Em seguida, encomende os metadados usando os valores desembrulhados assumidos.

    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
    }

É possível ter uma classificação secundária para opcionais. Por exemplo; era possível mostrar imagens com metadados e ordenadas por tamanho.

jnblanchard
fonte
1
Geralmente, as respostas são muito mais úteis se incluem uma explicação sobre o que o código pretende fazer e por que isso resolve o problema sem a introdução de outros.
Tom Aranda
Muito, muito melhor
Tom Aranda
18

Duas alternativas

1) Solicitando a matriz original com sortInPlace

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

2) Usando uma matriz alternativa para armazenar a matriz ordenada

var assignmentsO = [Assignment] ()
assignmentsO = self.assignments.sort({ $0.order < $1.order })
self.printAssignments(assignmentsO)
Bernauer
fonte
3
2) Qual é o sentido de construir uma matriz vazia e descartá-la na linha seguinte? Sugiro usá var assignmentsO : [Assignment]-lo ou combiná-lo em uma linha usandolet assignmentsO = self.assignments.sort({ $0.order < $1.order })
Hermann Klecker
2
Oi Hermann! Existe uma linha muito fina entre escrever código legível e eficiente. Nesse caso, o único ponto é torná-lo mais legível para a comunidade;) divirta-se!
Bernauer
18

Swift 4.0, 4.1 e 4.2 Primeiro, criei uma matriz mutável do tipo imageFile () como mostrado abaixo

var arr = [imageFile]()

Crie uma imagem mutável de objeto do tipo imageFile () e atribua valor às propriedades, como mostrado abaixo

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

Agora, anexe este objeto à matriz arr

    arr.append(image)

Agora, atribua as diferentes propriedades ao mesmo objeto mutável, ou seja, imagem

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

Agora, anexe novamente o objeto de imagem à matriz arr

    arr.append(image)

Agora, aplicaremos a ordem crescente na propriedade fileId nos objetos arr da matriz. Usar <símbolo para ordem crescente

 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

Agora, aplicaremos a ordem decrescente na propriedade fileId nos objetos arr da matriz. Use > símbolo para ordem decrescente

 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

No Swift 4.1. & 4.2 Para ordem classificada, use

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

Se você estiver classificando essa matriz em mais de um local, pode fazer sentido tornar seu tipo de matriz Comparável.

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]"
Kwerle
fonte
6

Se você não estiver usando objetos personalizados, mas tipos de valor que implementam o protocolo Comparable (Int, String etc.), você pode simplesmente fazer isso:

myArray.sort(>) //sort descending order

Um exemplo:

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
Dorian
fonte
em swift 3 émyArray.sorted(by: >)
berílio
6

Você retorna uma matriz classificada da propriedade fileID da seguinte maneira:

Swift 2

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

Swift 3 OU 4

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

Swift 5.0

let sortedArray = images.sorted {
    $0.fileID < $1.fileID
}
Vicky Prajapati
fonte
Funciona como um encanto .. votado! (Pratik Prajapati, Ahmedabad)
NSPratik
4

Eu faço assim e funciona:

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

Illya Krit
fonte
2

Se você deseja classificar a matriz original de objetos personalizados. Aqui está outra maneira de fazer isso no Swift 2.1

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

Onde idestá um número inteiro. Você também pode usar o mesmo <operador para Stringpropriedades.

Você pode aprender mais sobre seu uso consultando um exemplo aqui: Swift2: Clientes Próximos

Hanny
fonte
2
var students = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

students.sort(by: >)

print(students)

Impressões: "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"

Siddharth Chauhan
fonte
1

Swift 3 e 4 e 5

Tive algum problema relacionado a maiúsculas e minúsculas

então eu fiz esse código

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

e depois use o SortImages depois

Abdelrahman Mohamed
fonte
0

Classificar usando o KeyPath

você pode classificar KeyPathassim:

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

Ao implementar esta pequena extensão útil.

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

Hope Swift acrescenta isso em um futuro próximo no núcleo do idioma.

Mojtaba Hosseini
fonte
Isso já foi respondido aqui stackoverflow.com/a/46601105/2303865, juntamente com o método de mutação também.
Leo Dabus
a versão mutantepublic 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