LeetCodeの問題を解いていたんですが、sort
メソッドとsort_by
メソッドの違いがわからず解答に時間がかかったため、備忘録としてまとめます。
記事の信頼性
- ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
- 実務ではVue.jsとRailsを毎日書いています。
- 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
問題
LeetCodeの791. Custom Sort Stringという問題を解いており、次の解答を作成しました。
def custom_sort_string(order, s)
order_hash = Hash.new(0)
order.each_char.with_index do |char, i|
order_hash[char] = i
end
s.chars.sort { |char| order_hash[char] }.join
end
しかしこれだと、下記のテストケースで失敗してしまいます。
入力値: order = "exv", s = "xwvee"
実際の出力結果: "weevx"
期待する出力結果: "eexvw"
そこで解答のsort
メソッドの部分をsort_by
に変更したところ、すべてのテストケースで成功しました。
しかし、sort
とsort_by
の違いがわかっていないため、なぜこの変更でうまくいくようになったかが分かりませんでした、、。
解決方法
sort
とsort_by
のちがいを理解することで、納得できました。
sort メソッドの挙動
sort
メソッドは単に配列の要素同士を直接比較するだけです。
比較には<=>
演算子(宇宙船演算子)が使われます。
<=>
演算子は、二つの値を比較し、左辺が右辺より小さければ-1、等しければ0、大きければ1を返します。
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
puts numbers.sort.inspect
#=> [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]
fruits = ["banana", "apple", "pear", "orange"]
puts fruits.sort.inspect
#=> ["apple", "banana", "orange", "pear"]
なおinspect
メソッドは、オブジェクトを人間が読みやすい文字列形式で返します。
ここでは、配列の中身を見やすく表示するために使っています。
sort_by メソッドの挙動
sort_by
メソッドは、ブロックを渡して使います。
ブロックの評価結果をキーとして、要素どうしを並び替えます。
# 文字列の長さで昇順にソート
fruits = ["banana", "apple", "pear", "orange"]
puts fruits.sort_by { |fruit| fruit.length }.inspect
#=> ["pear", "apple", "banana", "orange"]
# ハッシュのバリューでソート
fruits_hash = { "banana" => 5, "apple" => 3, "pear" => 4, "orange" => 6 }
puts fruits_hash.sort_by { |_, value| value }.inspect
#=> [["apple", 3], ["pear", 4], ["banana", 5], ["orange", 6]]
冒頭のLeetCodeの解答の場合、order_hash[char]
の結果をキーにソートしたかったため、sort_by
を使うべきでした。
def custom_sort_string(order, s)
order_hash = Hash.new(0)
order.each_char.with_index do |char, i|
order_hash[char] = i
end
# sort ではなく sort_by を使う
s.chars.sort_by { |char| order_hash[char] }.join
end
これで無事に問題を解くことができました!
おわりに
アルゴリズム系の問題をソートを使って解く場合は、sort
とsort_by
をしっかり使い分けます。
- 比較キーが単純な場合(数値や文字列の大小関係など)は
sort
を使う。 - 比較キーが複雑な場合(別の値に基づいてソートするなど)は
sort_by
を使う。
コメント