うまくいかないアプローチ
まず最初に、期待する結果が得られないアプローチから確認しておきましょう。
次のようなリスト(List)があったとします。
val itemList = listOf(
"A-123",
"B-032",
"A-025",
"C-112",
"C-054",
"A-372",
"B-004",
"C-010",
"B-538"
)
これを、英字部分は昇順(A→C)、数字部分は降順(9→0)に並べ替えたいとしましょう。
つまり、期待する結果は下のようになります。
val expectedList = listOf(
"A-372",
"A-123",
"A-025",
"B-538",
"B-032",
"B-004",
"C-112",
"C-054",
"C-010"
)
各リストアイテムの文字列の英字もしくは数字部分を抽出し、それを元に単に昇順あるいは降順にするだけならば簡単です。
下のコードは、先頭文字(英字部分)を条件に、昇順に並び替えたものです。(コードが縦に長いので、スクロールするか、▼ボタンをクリックして展開してください)
val itemList = listOf(
"A-123",
"B-032",
"A-025",
"C-112",
"C-054",
"A-372",
"B-004",
"C-010",
"B-538"
)
fun main() {
val sortedList = itemList.sortedBy {
it.first()
}
for (item in sortedList) {
println(item)
}
/**
* Outputs
*
* A-123
* A-025
* A-372
* B-032
* B-004
* B-538
* C-112
* C-054
* C-010
*
*/
}
とりあえず、これで英字部分については期待通りの順番になりました。
しかし、この結果を利用して、続けて数字部分について次のように並べ替え(降順)を行っても、期待する結果にはなりません。
fun main() {
val sortedList = itemList.sortedBy {
it.first()
}.sortedByDescending {
it.slice(2..4)
}
for (item in sortedList) {
println(item)
}
/**
* Outputs
*
* B-538
* A-372
* A-123
* C-112
* C-054
* B-032
* A-025
* C-010
* B-004
*
*/
}
これは、英字部分について昇順で並べ替えが行われた後、その結果を全て上書きする形で数字部分について降順で並べ替えられてしまうからです。
当然、昇順→降順ではなく、降順→昇順のように処理を逆にしたところで、どのみち期待する結果は得られません。
並べ替えの条件として、昇順と降順を同時に設定したい場合は、次の章で紹介するアプローチが必要となります。
POINT!
- ・単純に昇順あるいは降順だけを条件に並び替えるのは簡単だが…!?
- ・昇順(降順)で並び替えた後、別の条件で並び替えを行うと全て上書きされてしまう!
- ・昇順と降順を同時に適用したい場合は、
sortedBy
や sortedByDescending
では難しい!
sortedWith()を使う
昇順と降順を並べ替えの条件として同時に適用させるには、sortedWith()
を使います。
そして、sortedWith()
の comparator
に渡す値として、compareBy()
や compareByDescending()
で昇順・降順の並べ替え条件を指定し、さらに thenBy()
や thenByDescending()
で、もう一つの昇順・降順の並べ替え条件を指定すれば、昇順と降順を同時に適用した結果を得ることができます。
Descendingという単語がつく方が降順で、つかない方が昇順です。
下は、itemList
に対し、sortedWith()
を使って昇順・降順を同時に適用し、期待する結果が得られるように並び替えた例です。(※縦に長いので以下略)
val itemList = listOf(
"A-123",
"B-032",
"A-025",
"C-112",
"C-054",
"A-372",
"B-004",
"C-010",
"B-538"
)
val expectedList = listOf(
"A-372",
"A-123",
"A-025",
"B-538",
"B-032",
"B-004",
"C-112",
"C-054",
"C-010"
)
fun main() {
val sortedList = itemList.sortedWith(
compareBy<String> {
it.first()
}.thenByDescending {
it.slice(2..4)
}
)
for (item in sortedList) {
println(item)
}
/**
* Outputs
*
* A-372
* A-123
* A-025
* B-538
* B-032
* B-004
* C-112
* C-054
* C-010
*
*/
println(expectedList == sortedList) // Outputs: true
}
ということで、無事に期待通りの結果が得られていることが確認できました!
thenBy()
や thenByDescending()
で処理を繋げる場合、compareBy()
や compareByDescending()
において、データの型を明示的に指定しておく必要があります。
よって、次のコードはエラーになります。
val sortedList = itemList.sortedWith(
compareBy { // This code will cause an error
it.first()
}.thenByDescending {
it.slice(2..4)
}
)
今回はリストアイテムを文字列型(String)で管理しているため、<String>
と指定しておけばOKです。
ただし、これまで例として使用してきたリストのように、単に文字列が配列になっているだけのリストを扱うケースは、実際には稀であると思います。
実際のアプリ開発では、data classなどによって定義された文字列、整数、真偽値などを利用し、複数の要素を含んだリストを管理することが多いです。
たとえば、ユーザー情報(IDとパスワード)を管理する場合、IDを整数、パスワードを文字列として data class で設定し、それらをまとめてユーザー情報のリストとして管理する…といった感じですね。
ということで、次の章では data class を利用してリストアイテムが管理されている場合を例に、同じように昇順と降順を同時に適用させて並び替えを行ってみたいと思います。
POINT!
- ・昇順と降順を同時に適用させるには、sortedWith()を使う!
- ・compareBy()やcompareByDescending()で最初の並び替えを行う!
- ・thenBy()やthenByDescending()で次の並び替えを行う!
data classでリストが管理されている場合
data classを利用してリストが管理されている場合も、前章で紹介した方法とほぼ同様の手順で昇順・降順が混在した並べ替えを行うことが可能です。
例として、次のような data class
とリストがあったとしましょう。
data class Item (
val group: String,
val num: Int
)
val itemList = listOf(
Item("A", 123),
Item("B", 32),
Item("A", 25),
Item("C", 112),
Item("C", 54),
Item("A", 372),
Item("B", 4),
Item("C", 10),
Item("B", 538),
)
そして今度は、英字(group)を降順にした上で、数字(num)を昇順にして並び替えを行ってみたいと思います。
つまり、compareByDescending()
で group
部分について降順で並び替えを行い、続けて thenBy()
で num
部分について昇順で並び替えを行うということですね。
コードと実行結果は次のようになります。
fun main() {
val sortedList = itemList.sortedWith(
compareByDescending<Item> {
it.group
}.thenBy {
it.num
}
)
for (item in sortedList) {
println("${item.group}: ${item.num}")
}
/**
* Outputs
*
* C: 10
* C: 54
* C: 112
* B: 4
* B: 32
* B: 538
* A: 25
* A: 123
* A: 372
*/
}
ということで、data classを利用して管理されているリストの並べ替えについても、期待通りの結果を得ることができました!
なお、今回のコードの肝である sortedWith()
について詳しい情報を得たい方は、公式ドキュメントをご参考ください。
Kotlinの公式サイトは英語表記ですが、Kotlinに限らずプログラミングの世界では公式ドキュメントの表記言語が英語であることが多いので、英語で書かれたページから情報を得ることに慣れておいた方が良いです。
もちろん翻訳ツールを頼っても良いので、興味を持って色々読んでみることをオススメします!
POINT!
- ・data classでリストが管理されている場合も、ほぼ同じ手順で昇順・降順が混在した並び替えができる!
- ・より詳しい仕様や情報が必要な場合は、公式ドキュメントを読み込もう!
- ・公式ドキュメントは英語表記の場合が多いので、英文を読むことに抵抗をなくしておこう!