web-dev-qa-db-ja.com

Ruby 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のようなものにすると、配列の最後に配置される可能性が高くなりますが、この解決策は任意なので、私は好きではありません

39
Chris Drappier

Childが存在する場合にcategoryが存在する場合は<=>に基づいてcategory.positionを定義し、categoryのない項目を常により大きく並べ替えるcategory

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

次に、Parentchildren.sortを呼び出すだけです。

16
Brian Campbell

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の場合は、1つ大きい順に並べ替えるステータスを返します。

106
glenra

私はこれらの種類のものをこのように扱います:

 children.sort_by {|child| [child.position ? 0 : 1,child.position || 0]}
17
glenn mcdonald

公平を期すために、私はRubyにあまり慣れていないので、これをコードではなくアルゴリズムのアイデアと考えてください。そして、::演算子を次のように書き換えてくださいRubyクリーナー。

比較でnilをチェックすることはできませんか?

class Parent
  def sorted_children
     children.sort{|a,b|( a and b ) ? a <=> b : ( a ? -1 : 1 ) }
  end
end

Glenraのコードを使用するように編集しました。これは、私のものと同じものを実装しますが、コードの量が少なく(おそらく読みやすく)なります。

6
RHSeeger

私にとって最も簡単な解決策は

def sorted_children(children)
  children.sort_by { |child| child.position || -1}
end
1
Dolev

これは、新しい比較メソッドを定義することにより、宇宙船オペレーターをオーバーライドせずに行うことができます。

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) }
1

Rubyはしばらく実行していませんが、並べ替えからnullチェックを分割できます(Child#positionがnullを返すことを許可するだけです)。

def sorted_children
  children.reject{|c| c.position.nil?}.sort_by(&:position) +
    children.select{|c| c.position.nil?}
end

確かに、これは最も効率的なソリューションではありませんが、マジックナンバーがありません。

1
Ken