オブジェクトのルビー配列を、nilの可能性がある属性でソートする

StackOverflow https://stackoverflow.com/questions/808318

  •  03-07-2019
  •  | 
  •  

質問

整数または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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top