比較関数つきソート
「小さい順」が、欲しい並びとは限らない
並べ替えの道具は「小さい順」を知っています。でも実際に欲しいのは、価格の安い順、文字数の短い順、絶対値の小さい順のように、値そのものではない別のものさしで測った順序であることがほとんどです。
[3, -1, -4, 1] を絶対値の小さい順にすると [-1, 1, 3, -4] です。値の大小で並べた [-4, -1, 1, 3] とはまったく違います。だからといって道具を作り直す必要はありません。並べ替えには、何で比べるかを渡せるようになっています。
比べる前に、測り直したものを渡す
Python の sorted は key を受け取ります。各要素を 1 度だけ key に通し、返ってきた値のほうで並べます。
Python
words = ["pear", "fig", "apple", "kiwi"]
print(sorted(words, key=len))
# ['fig', 'kiwi', 'pear', 'apple']len が返した 4, 3, 5, 4 で並んだ結果です。元の文字列そのものは一度も比べていません。
JavaScript は流儀が少し違い、2 つを受け取ってどちらを前にするかを数値で答える関数を渡します。負なら第 1 引数が前、正なら第 2 引数が前、0 なら決めない、という約束です。
JavaScript
const words = ["pear", "fig", "apple", "kiwi"];
console.log([...words].sort((a, b) => a.length - b.length));
// ['fig', 'pear', 'kiwi', 'apple']引き算の結果がそのまま負・0・正になるので、a.length - b.length の 1 行で条件を書き切れます。順序を逆にしたいなら引く向きを入れ替えます。
測った値が同じになったとき、誰も順番を決めていない
fig と ant はどちらも 3 文字です。len はどちらにも 3 を返すので、この 2 つのどちらを前にするかを、ものさしは何も言っていません。JavaScript の書き方でいえば 0、つまり「決めない」と答えたのと同じです。
ここを自分で決めたいなら、ものさしが 1 つでは足りないと考えます。1 つ目で差がつかなかったときに見る 2 つ目を用意するのです。文字数が同じならアルファベット順、絶対値が同じなら元の値の小さい順。引き分けの決着のつけ方まで含めて、はじめて順序が定まります。
決着のルールを書いておく価値はもう 1 つあります。何も書かなければ、同じ値どうしの並びは道具の実装まかせになり、言語やバージョンが変わったときに結果が変わり得ます。意図があるなら明示してください。
よくある間違い
JavaScript の比較関数で (a, b) => a.length < b.length のように真偽値を返すことです。true は 1、false は 0 として扱われるため、「第 2 引数が前」と「決めない」の 2 通りしか伝わりません。並びは崩れるのに例外は出ないので、気づきにくい間違いです。必ず数値の差を返してください。
要件
- ソートの基準は 1) 絶対値が小さい順 2) 同じ絶対値なら元の値が小さい順
- Python / JS / Go の組み込みソートを使ってよいが、必ず key 関数または比較関数を渡すこと
- 戻り値は新しい配列で、入力 arr を破壊的に書き換えない
入出力例
sortByKey([3,-1,-4,1,5,-9,2,6]) → [-1,1,2,3,-4,5,6,-9]
sortByKey([5,3,1,4,2]) → [1,2,3,4,5]
sortByKey([-3,-1,-2]) → [-1,-2,-3]
sortByKey([1,-1]) → [-1,1]
sortByKey([0,-1,1,-2,2]) → [0,-1,1,-2,2]
sortByKey([-7]) → [-7]