LoginSignup
2
0

Enumerable#sort_by の comparison of Array with Array failed (ArgumentError) というエラーはどのような場合に発生するか

Last updated at Posted at 2023-06-29

バージョン情報

$ ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]

Enumerable#sort_by のブロックの評価結果が配列で、かつ配列の要素に nil を含む場合に "comparison of Array with Array failed" というエラーが発生する。

Pokemon = Data.define(:name, :type1_id, :type2_id)

# 例 1: OK
pokemons = [
  Pokemon.new(name: 'ジュナイパー', type1_id: 5, type2_id: 14),
  Pokemon.new(name: 'ゲッコウガ', type1_id: 3, type2_id: 16),
  Pokemon.new(name: 'フシギバナ', type1_id: 5, type2_id: 8)
]
pokemons.sort_by { [_1.type1_id, _1.type2_id] }.map(&:name)
#=> ["ゲッコウガ", "フシギバナ", "ジュナイパー"]

# 例 2: OK
pokemons = [
  Pokemon.new(name: 'ジャローダ', type1_id: 5, type2_id: nil),
  Pokemon.new(name: 'ゲッコウガ', type1_id: 3, type2_id: 16),
  Pokemon.new(name: 'バクフーン', type1_id: 2, type2_id: nil)
]
pokemons.sort_by { [_1.type1_id, _1.type2_id] }.map(&:name)
#=> ["バクフーン", "ゲッコウガ", "ジャローダ"]

# 例 3: NG
pokemons = [
  Pokemon.new(name: 'ジャローダ', type1_id: 5, type2_id: nil),
  Pokemon.new(name: 'ゲッコウガ', type1_id: 3, type2_id: 16),
  Pokemon.new(name: 'フシギバナ', type1_id: 5, type2_id: 8)
]
pokemons.sort_by { [_1.type1_id, _1.type2_id }.map(&:name)
# `sort_by': comparison of Array with Array failed (ArgumentError)

理由

ブロックの評価結果の配列に nil が含まれる場合、かつ nil と nil でないオブジェクトの比較が行われる際にエラーが発生する。例えば上記のコードでは、例 1 と例 2 の sort_by では nil と nil でないオブジェクトの比較は行われない。しかし例 3 の場合は行われる。

Enumerable#sort_by では値の比較時に <=> 演算子 (比較演算子) を使用する。この <=> は比較可能な場合は整数値を返し、不可能な場合は nil を返す。例えば nil と整数値は比較ができない。

module Comparable (Ruby 3.2 リファレンスマニュアル) より

self <=> other は

  • self が other より大きいなら正の整数
  • self と other が等しいなら 0
  • self が other より小さいなら負の整数
  • self と other が比較できない場合は nil

をそれぞれ返すことが期待されています。

[5, nil] <=> [5, 8]
#=> nil

5 <=> 5
#=> 0

nil <=> 8
#=> nil

ちなみに true と false も比較できない。

false <=> true
#=> nil

[[2, true], [1, false], [2, false]].sort_by(&:itself)
# `sort_by': comparison of Array with Array failed (ArgumentError)

どうしても比較したいのであれば、比較可能なオブジェクト同士 (整数と整数など) で比較するように工夫するとよい。

pokemons = [
  Pokemon.new(name: 'ジャローダ', type1_id: 5, type2_id: nil),
  Pokemon.new(name: 'ゲッコウガ', type1_id: 3, type2_id: 16),
  Pokemon.new(name: 'フシギバナ', type1_id: 5, type2_id: 8)
]
pokemons.sort_by { [_1.type1_id, _1.type2_id }.map(&:name)
# `sort_by': comparison of Array with Array failed (ArgumentError)
pokemons.sort_by { [_1.type1_id, (_1.type2_id || 0)] }.map(&:name)
#=> ["ゲッコウガ", "ジャローダ", "フシギバナ"]

[[2, true], [1, false], [2, false]].sort_by(&:itself)
# `sort_by': comparison of Array with Array failed (ArgumentError)
[[2, true], [1, false], [2, false]].sort_by { [_1, _2 ? 1 : 0] }
#=> [[1, false], [2, false], [2, true]]
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0