タイトルの通りです。
普段はDBからselectする時にorder byすることが多い気がしますが、AtCoderをやっていてプログラミング言語側でちょっと複雑なソートをしたくなったので調べました。
解いていた問題
解法メモ
- 問題文の通りにソートする
List<String>
とかList<Int>
とかであれば、list.sorted()
とかでソートが出来るけど、今回は複数項目をキーにしたソートをしたいlist.sortWith(compareBy(~~~))
、list.sortWith(compareByDescending(~~~))
を使ってあげると良さそう
確認(の事前準備)
前提となるデータを以下の感じで準備しました。
class Person(val no: Int, val group: Int, val firstName: String, val lastName: String) val list = mutableListOf<Person>() list.add(Person(1, 1, "たろう", "さとう")) list.add(Person(2, 1, "じろう", "すずき")) list.add(Person(3, 1, "さぶろう", "たかはし")) list.add(Person(4, 2, "しろう", "たなか")) list.add(Person(5, 2, "ごろう", "いとう")) list.add(Person(6, 2, "ろくろう", "いとう"))
確認1 数値項目 昇順
以下のようにorder byの感覚で指定できました。
list.sortWith(compareBy({ it.group }, { it.no })) println("list.sortWith(compareBy({ it.group }, {it.no}))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println()
↓出力
list.sortWith(compareBy({ it.group }, {it.no})) 1 1 たろう さとう 2 1 じろう すずき 3 1 さぶろう たかはし 4 2 しろう たなか 5 2 ごろう いとう 6 2 ろくろう いとう
確認2 数値項目 降順
以下のように、確認1で指定した項目にマイナスをつけてあげると逆順にソートすることが出来るようでした。
// 数値項目ならマイナスすれば降順になる list.sortWith(compareBy({ -it.group }, { it.no })) println("list.sortWith(compareBy({ -it.group }, { it.no }))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println() list.sortWith(compareBy({ -it.group }, { -it.no })) println("list.sortWith(compareBy({ -it.group }, { -it.no }))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println()
↓出力
list.sortWith(compareBy({ -it.group }, { it.no })) 4 2 しろう たなか 5 2 ごろう いとう 6 2 ろくろう いとう 1 1 たろう さとう 2 1 じろう すずき 3 1 さぶろう たかはし list.sortWith(compareBy({ -it.group }, { -it.no })) 6 2 ろくろう いとう 5 2 ごろう いとう 4 2 しろう たなか 3 1 さぶろう たかはし 2 1 じろう すずき 1 1 たろう さとう
確認3 文字列項目 降順
昇順は数値項目同様にcompareByでソートできますが、文字列の降順はマイナスを付けてもビルドが通りません。
文字列の場合はcompareByDescendingを使用することで降順ソートができました。
// 文字列項目の降順はcompareByDescending list.sortWith(compareByDescending({ it.firstName })) println("list.sortWith(compareByDescending({ it.firstName }))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println()
↓出力
list.sortWith(compareByDescending({ it.firstName })) 6 2 ろくろう いとう 1 1 たろう さとう 2 1 じろう すずき 4 2 しろう たなか 3 1 さぶろう たかはし 5 2 ごろう いとう
確認4 文字列項目 降順含む複数
例えば「lastNameを降順にしたくて、lastName同一のものはfirstName昇順で並べたい」というケースを考えた時、確認1~3だけでは上手くできなさそうな気がして困りました。
試しに二度sortWithをかませてみたらそれっぽくなりましたが、並び順が保証されているかは未確認です。*1
// 文字列項目を複数指定したい場合はcompareByDescending * 2で一応できるけど・・・ list.sortWith(compareByDescending({ it.firstName })) list.sortWith(compareByDescending({ it.lastName })) println("list.sortWith(compareByDescending({ it.firstName }))") println("list.sortWith(compareByDescending({ it.lastName }))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println() list.sortWith(compareBy({ it.firstName })) list.sortWith(compareByDescending({ it.lastName })) println("list.sortWith(compareBy({ it.firstName }))") println("list.sortWith(compareByDescending({ it.lastName }))") for (item in list) { println("${item.no} ${item.group} ${item.firstName} ${item.lastName}") } println()
↓出力
list.sortWith(compareByDescending({ it.firstName })) list.sortWith(compareByDescending({ it.lastName })) 4 2 しろう たなか 3 1 さぶろう たかはし 2 1 じろう すずき 1 1 たろう さとう 6 2 ろくろう いとう 5 2 ごろう いとう list.sortWith(compareBy({ it.firstName })) list.sortWith(compareByDescending({ it.lastName })) 4 2 しろう たなか 3 1 さぶろう たかはし 2 1 じろう すずき 1 1 たろう さとう 5 2 ごろう いとう 6 2 ろくろう いとう
↓確認してみたコードは以下で作ってあるので、少しいじって試したりなどできると思います。 https://pl.kotl.in/A__PBXw3k?theme=darcula
提出した回答
*1:もっと確実&スマートな方法があれば教えてください