2016-08-08 34 views
3

Ich verwende sortedBy(), um die Sortierung der Objektsammlung durchzuführen.Verwenden der Bedingung zum Auswählen der Sortiereigenschaft in Kotlin

Da die Reihenfolge auf der Benutzer je nach Wahl kann sich ändern, ich habe

mit dem folgenden Code am Ende
val sortedList = if (sortingOrder == WordSortingOrder.BY_ALPHA) { 
        list.sortedBy { it.word.value } 
       } else { 
        list.sortedBy { it.createdAt } 
       } 

Dann führe ich auf der sortierten Sammlung weitere Aktionen. Ich merke, dass sortedBy() Methode erwartet, dass eine Eigenschaft zurückgegeben wird. Ich frage mich, ob es eine Möglichkeit gibt, die Sortierbedingung in eine Kette von Sammelmethoden einzubetten.

Antwort

3

Wenn Ihre Eigenschaften verschiedenen Typen sind, werden Sie nicht in der Lage sein, eine von ihnen für sortedBy auf einer Bedingung als Ergebnis basierend auszuwählen, als ihr gemeinsamer geordneter Typ würde als Any geschlossen werden und es ist nicht ein Subtyp von Comparable<R> als sortedBy erwartet.

Stattdessen können Sie sortedWith Methode verwenden, die eine Comparator nimmt, und einen Komparator unter der Bedingung abhängig liefern:

list.sortedWith(
    if (sortingOrder == WordSortingOrder.BY_ALPHA) 
     compareBy { it.word.value } 
    else 
     compareBy { it.createdAt } 
) 

Komparatoren für verschiedene Eigenschaften hier mit der Funktion kotlin.comparisons.compareBy erstellt werden. Jede ist kein Subtyp von Vergleichbaren ` ` gefolgert Typ:

Sie können dann die Logik extrahieren, den Komparator auf Sortierreihenfolge auf eine Funktion basierend wählt:

list.sortedWith(comparatorFor(sortingOrder)) 

fun comparatorFor(sortingOrder: WordSortingOrder): Comparator<MyType> = ... 
1

Die sortedBy erwartet jede Funktion des Typs (T) -> R als Parameter. Eine Eigenschaft ist ein Eckfall davon.

Was bedeutet, dass Sie dies tun können:

val sortedList = list 
    .sortedBy { if (sortingOrder == WordSortingOrder.BY_ALPHA) it.word.value else it.createdAt} 

Oder, wenn Sie etwas mehr OOP-ish brauchen:

enum class WordSortingOrder(val transform: (MyObject) -> Int) { 
    BY_ALPHA({it.word.value}), 
    BY_ALPHA_REVERSED({-1 * it.word.value}), 
    DEFAULT({it.createdAt}) 
} 

val sortedList = list.sortedBy { sortingOrder.transform(it)} 
0

können Sie so etwas wie:

list.sortedBy { item -> 
    when(sortingOrder) { 
     WordSortingOrder.BY_ALPHA -> item.word.value 
     else      -> item.createdAt 
    } 
} 
0

Sie kann das Lambda-Argument übergeben an sortedBy Bedingung:

list.sortedBy(if (sortingOrder == WordSortingOrder.BY_ALPHA) { 
    { it: MyType -> it.word.value } 
} else { 
    { it: MyType -> it.createdAt } 
}) 

Sie finden when statt if besser lesbar in diesem Szenario mit:

list.sortedBy(when (sortingOrder) { 
    WordSortingOrder.BY_ALPHA -> { it: MyType -> it.word.value } 
    else -> { it: MyType -> it.createdAt } 
}) 

Wenn Ihre Wähler verschiedene Rückgabetypen haben, dann können Sie einfach Ihre vorhandenen Code in list.let { list -> ... } wickeln oder run verwenden:

list.run { 
    if (sortingOrder == WordSortingOrder.BY_ALPHA) { 
     sortedBy { it.word.value } 
    } else { 
     sortedBy { it.createdAt } 
    } 
} 

Sie können dann Anrufe weiterleiten nach der let/run.

+0

Für mich diesen Code zeigt einen Compiler-Fehler Ich denke, das passiert, weil 'it.word.value' und' it.createdAt' hat verschiedene Rückgabetypen. – Zzokk

+0

Da die Eigenschaften unterschiedliche Typen haben, müssen die Rückgabewerte in 'Comparable 'umgewandelt werden, um Compilerfehler zu vermeiden. Mit dem Casting sieht es nicht mehr so ​​gut aus, daher ist es wahrscheinlich eine bessere Lösung, den Code wie in der Frage zu haben. Es erlaubt auch leicht zwischen aufsteigender und absteigender Reihenfolge zu wechseln. – Zzokk

+0

Guter Punkt @ Zzokk. Das habe ich vorher nicht bedacht. Ich habe ein weiteres Beispiel hinzugefügt, das 'run' verwendet. Sie können im Grunde jede Gruppe von Operationen in einen "let" - oder "run" -Block einschließen und danach weiter verketten. – mfulton26