Swift comment trier un tableau d'objets personnalisés par valeur de propriété

521

disons que nous avons une classe personnalisée nommée imageFile et cette classe contient deux propriétés.

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

beaucoup d'entre eux stockés dans Array

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)

la question est: comment puis-je trier le tableau d'images par 'fileID' ASC ou DESC?

mohacs
la source
trier par KeyPath stackoverflow.com/a/46601105/2303865
Leo Dabus

Réponses:

941

Tout d'abord, déclarez votre tableau en tant que tableau typé afin de pouvoir appeler des méthodes lorsque vous itérez:

var images : [imageFile] = []

Ensuite, vous pouvez simplement faire:

Swift 2

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

Swift 3+

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

L'exemple ci-dessus donne l' ordre de tri desc

Alex Wayne
la source
1
Il me manquait une partie de déclaration de tableau, cela a fait l'affaire Array <imageFile>.
mohacs
1
@AlexWayne J'ai une NSManagedObjectsous - classe appelée CheckInAndOut . Et dans un fichier séparé, j'ai déclaré un tableau typé pour les objets de ce type et lorsque j'essaie de le trier , j'obtiens une erreur de membre introuvable . Une idée pourquoi c'est?
Isuru
3
J'ai trouvé mon problème. Apparemment, le tableau n'était pas un tableau typé. Quoi qu'il en soit, j'ai un nouveau problème. Comment trier un tableau selon plusieurs propriétés? Disons que j'ai 2 propriétés comme firstNameet lastNamedans un tableau d' Personobjets. Je veux d'abord trier par firstNameet ensuite lastName. Comment puis-je faire cela?
Isuru
12
devez-vous faire maintenant images.sortInPlace({ $0.fileID > $1.fileID })?
Taylor M
13
au cas où quelqu'un se demanderait la même chose: la réponse donnera l'ordre de desc
Danny Wang
223

[ Mis à jour pour Swift 3 avec sort (par :) ] Ceci, exploitant une fermeture de fin:

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

où vous utilisez <ou >selon ASC ou DESC, respectivement. Si vous souhaitez modifier le imagestableau , utilisez ce qui suit:

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

Si vous allez le faire à plusieurs reprises et que vous préférez définir une fonction, l'une des façons est la suivante:

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

puis utiliser comme:

images.sort(by: sorterForFileIDASC)
GoZoner
la source
comment puis-je poursuivre cela avec une chaîne? j'ai besoin de trier la chaîne par sa longueur
Muneef M
@MuneefM vient de renvoyer string1.length <string2.length
Surjeet Rajput
sortne compile plus avec cette syntaxe dans Xcode 8. Xcode 8 dit $0.fileID < $1.fileIDproduit un Bool et non ComparisonResult.
Crashalot
3
Le code de cette réponse fonctionne bien dans Xcode8; si vous avez une erreur, postez une nouvelle question.
GoZoner
Puis-je également l'utiliser pour trier par comparaisons, par exemple en ayant le tableau à trier par jours de la semaine? si c'est le cas, comment?
Kristofer
53

Presque tout le monde donne comment directement, permettez-moi de montrer l'évolution:

vous pouvez utiliser les méthodes d'instance d'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 })

Pour des explications détaillées sur le principe de fonctionnement du tri, voir La fonction triée .

fujianjin6471
la source
Puis-je également l'utiliser pour trier par comparaisons, par exemple en ayant le tableau à trier par jours de la semaine? si c'est le cas, comment?
Kristofer
Merci d'avoir posté une réponse qui montre comment fonctionne une fermeture au lieu de supposer que les lecteurs comprennent la syntaxe cryptique de la fermeture "simplifiée"!
user1118321
50

Swift 3

people = people.sorted(by: { $0.email > $1.email })
quemeful
la source
J'ai essayé cela avec une comparaison de dates, je n'ai pas pu le faire fonctionner. Une idée?
Ebru Güngör
Pas NSDate ou String, objet Swift 3 Date actuel.
Ebru Güngör
Quelle propriété de Date comparez-vous? La propriété doit pouvoir être comparée avec la fonction utilisée (supérieure à celle de mon exemple)
quemeful
9
C'est la seule réponse utile à partir de 2017.
Fattie
@Fattie Que voulez-vous dire? La syntaxe correcte estpeople.sort { $0.email > $1.email }
Leo Dabus
43

Avec Swift 5, Arraya deux méthodes appelées sorted()et sorted(by:). La première méthode, sorted()a la déclaration suivante:

Renvoie les éléments de la collection, triés.

func sorted() -> [Element]

La deuxième méthode, sorted(by:)a la déclaration suivante:

Renvoie les éléments de la collection, triés en utilisant le prédicat donné comme comparaison entre les éléments.

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

#1. Trier par ordre croissant pour des objets comparables

Si le type d'élément de votre collection est conforme au Comparableprotocole, vous pourrez l'utiliser sorted()pour trier vos éléments par ordre croissant. Le code Playground suivant montre comment utiliser 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. Trier par ordre décroissant pour des objets comparables

Si le type d'élément à l'intérieur de votre collection est conforme au Comparableprotocole, vous devrez utiliser sorted(by:)afin de trier vos éléments avec un ordre décroissant.

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. Trier par ordre croissant ou décroissant pour les objets non comparables

Si le type d'élément dans votre collection n'est PAS conforme au Comparableprotocole, vous devrez l'utiliser sorted(by:)pour trier vos éléments par ordre croissant ou décroissant.

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

Notez que Swift fournit également deux méthodes appelées sort()et en sort(by:)tant que contreparties de sorted()et sorted(by:)si vous devez trier votre collection sur place.

Imanou Petit
la source
25

Dans Swift 3.0

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

Vous pouvez également faire quelque chose comme

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

de sorte que votre tableau d'images sera stocké comme trié

Nicolas Grenié
la source
19

Swift 2 à 4

La réponse d'origine visait à trier un tableau d'objets personnalisés à l'aide d'une propriété. Ci-dessous, je vais vous montrer quelques façons pratiques de faire ce même comportement avec des structures de données rapides!

De petites choses sur le chemin, j'ai changé ImageFile très légèrement. Dans cet esprit, je crée un tableau avec trois fichiers image. Notez que les métadonnées sont une valeur facultative, passant nil car un paramètre est attendu.

 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 a une propriété nommée taille. Pour les exemples suivants, je vais vous montrer comment utiliser les opérations de tri avec des propriétés comme la taille.

du plus petit au plus grand (<)

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

du plus grand au plus petit (>)

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

Ensuite, nous allons trier en utilisant le nom de la propriété String. De la même manière, utilisez sort pour comparer les chaînes. Mais notez que le bloc interne renvoie un résultat de comparaison. Ce résultat définira le tri.

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
    }

Ensuite, c'est ma façon préférée de trier, dans de nombreux cas, on aura des propriétés facultatives. Maintenant ne vous inquiétez pas, nous allons trier de la même manière que ci-dessus, sauf que nous devons gérer zéro! En production;

J'ai utilisé ce code pour forcer toutes les instances de mon tableau avec des valeurs de propriété nil à être en dernier. Ensuite, triez les métadonnées en utilisant les valeurs non encapsulées supposées.

    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
    }

Il est possible d'avoir un tri secondaire pour les options. Par exemple; on pourrait montrer des images avec des métadonnées et classées par taille.

jnblanchard
la source
1
Généralement, les réponses sont beaucoup plus utiles si elles incluent une explication de ce que le code est censé faire et pourquoi cela résout le problème sans en introduire d'autres.
Tom Aranda
Beaucoup mieux.
Tom Aranda
18

Deux alternatives

1) Commande du tableau d'origine avec sortInPlace

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

2) Utilisation d'un tableau alternatif pour stocker le tableau ordonné

var assignmentsO = [Assignment] ()
assignmentsO = self.assignments.sort({ $0.order < $1.order })
self.printAssignments(assignmentsO)
Bernauer
la source
3
re 2) Quel est l'intérêt de construire un tableau vide et de le jeter dans la ligne suivante? Je suggérerais de l'utiliser var assignmentsO : [Assignment]ou de le combiner en une seule ligne en utilisantlet assignmentsO = self.assignments.sort({ $0.order < $1.order })
Hermann Klecker
2
Salut Hermann! Il existe une ligne très mince entre l'écriture de code lisible et efficace. Dans ce cas, le seul point est de le rendre plus lisible pour la communauté;) profitez-en!
Bernauer
18

Swift 4.0, 4.1 & 4.2 Tout d'abord, j'ai créé un tableau mutable de type imageFile () comme indiqué ci-dessous

var arr = [imageFile]()

Créez une image objet mutable de type imageFile () et affectez une valeur aux propriétés comme indiqué ci-dessous

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

Maintenant, ajoutez cet objet au tableau arr

    arr.append(image)

Maintenant, affectez les différentes propriétés au même objet mutable, c'est-à-dire l'image

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

Maintenant, ajoutez à nouveau l'objet image au tableau arr

    arr.append(image)

Maintenant, nous allons appliquer l' ordre croissant sur la propriété fileId dans les objets tableau arr. Utilisez le symbole < pour l'ordre croissant

 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

Maintenant, nous allons appliquer l' ordre décroissant sur la propriété fileId dans les objets tableau arr. Utilisez le symbole> pour l'ordre décroissant

 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

Dans Swift 4.1. & 4.2 Pour une utilisation par ordre trié

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

Si vous allez trier ce tableau à plusieurs endroits, il peut être judicieux de rendre votre type de tableau 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]"
kwerle
la source
6

Si vous n'utilisez pas d'objets personnalisés, mais plutôt des types de valeur qui implémentent un protocole comparable (Int, String etc.), vous pouvez simplement le faire:

myArray.sort(>) //sort descending order

Un exemple:

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
la source
dans swift 3 c'estmyArray.sorted(by: >)
béryllium
6

Vous retournez un tableau trié à partir de la propriété fileID de la manière suivante:

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
la source
Fonctionne comme un charme .. voté! (Pratik Prajapati, Ahmedabad)
NSPratik
4

Je le fais comme ça et ça marche:

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

Illya Krit
la source
2

Si vous souhaitez trier le tableau d'origine d'objets personnalisés. Voici une autre façon de le faire dans Swift 2.1

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

idest un entier. Vous pouvez également utiliser le même <opérateur pour les Stringpropriétés.

Vous pouvez en savoir plus sur son utilisation en consultant un exemple ici: Swift2: Clients à proximité

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

students.sort(by: >)

print(students)

Tirages: "["Peter", "Kweku", "Kofi", "Akosua", "Abena"]"

Siddharth Chauhan
la source
1

Swift 3 & 4 & 5

J'ai eu un problème lié aux minuscules et aux majuscules

donc j'ai fait ce code

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

puis utilisez sortedImages après cela

Abdelrahman Mohamed
la source
0

Trier à l'aide de KeyPath

vous pouvez trier KeyPathcomme ceci:

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

En implémentant cette petite extension utile.

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 ajoutera cela dans un avenir proche au cœur de la langue.

Mojtaba Hosseini
la source
Cela a déjà été répondu ici stackoverflow.com/a/46601105/2303865 ainsi que la méthode de mutation.
Leo Dabus
la version 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