オブジェクトのルビー配列を、nilの可能性がある属性でソートする
質問
整数またはnilの可能性がある位置属性でソートする必要があるオブジェクトの配列があり、配列の最後にnil位置を持つオブジェクトが必要です。ここで、array.sortが失敗しないように、nilではなく値を返すように位置を強制できますが、このデフォルトとして0を使用すると、それらのオブジェクトがソートの前に配置されます。このようなことをする最良の方法は何ですか? 「ほとんど」常に最後にあることが保証されているばかげたほど高い数にnil値を設定する必要がありますか?または、array.sortメソッドにnil属性オブジェクトを配列の最後に配置させる他の方法がありますか?コードは次のようになります。
class Parent
def sorted_children
children.sort{|a, b| a.position <=> b.position}
end
end
class Child
def position
category ? category.position : #what should the else be??
end
end
今、「else」を1000000000のようにすると、配列の最後に配置する可能性が高くなりますが、このソリューションはarbitrary意的であるため気に入らないでしょう
解決
Child
が存在する場合、<=>
に基づいてcategory.position
を定義し、category
のないアイテムよりも常に大きいParent
のないアイテムをソートする方法についてはどうですか?
class Child
# Not strictly necessary, but will define other comparisons based on <=>
include Comparable
def <=> other
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
その後、children.sort
で<=>を呼び出すことができます。
他のヒント
nil
アイテムを最後に置くように並べ替えを調整します。このようなものを試してください。
foo = [nil, -3, 100, 4, 6, nil, 4, nil, 23]
foo.sort { |a,b| a && b ? a <=> b : a ? -1 : 1 }
=> [-3, 4, 4, 6, 23, 100, nil, nil, nil]
つまり、aとbが両方とも非nilの場合は通常に並べ替えますが、どちらかがnilの場合は、大きい方を並べ替えるステータスを返します。
次のようなこれらの種類を処理します:
children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
公平を期すために、私はRubyにあまり詳しくないので、これをコード1というよりもアルゴリズムのアイデアとして捉え、Rubyがよりクリーンなものとして?:演算子を書き直します。
比較でnilだけをチェックすることはできません:
class Parent
def sorted_children
children.sort{|a,b|( a and b ) ? a <=> b : ( a ? -1 : 1 ) }
end
end
Glenraのコードを使用するように編集しました。これは、私のものと同じことを実装しますが、より少ない(そしておそらく読みやすい)コードで実装します。
しばらくRubyを実行していませんが、nullチェックと並べ替えを分割できます(Child#positionがnullを返すようにするだけです):
def sorted_children
children.reject{|c| c.position.nil?}.sort_by(&:position) +
children.select{|c| c.position.nil?}
end
確かに、これは最も効率的なソリューションではありませんが、マジックナンバーはありません。
新しい比較方法を定義することにより、宇宙船オペレーターをオーバーライドせずにこれを行うことができます。
class Child
include Comparable
def compare_by_category(other)
return 0 if !category && !other.category
return 1 if !category
return -1 if !other.category
category.position <=> other.category.position
end
end
sort
メソッドはブロックを取ることができるため、この新しいメソッドを使用してソートできます。
children.sort {|a,b| a.compare_by_category(b) }
私にとって最も簡単な解決策は
def sorted_children(children)
children.sort_by { |child| child.position || -1}
end