我有一个place
具有以下参数的对象:phone
, category
, street
, zip
, website
.
我还有一个place
对象数组:[place1, place2, place3, place4, place5]
.
place
根据参数可用性对 s 数组进行排序的最佳方法是什么?即,如果place1
有最多可用的参数,或最少的参数nil
,它应该重新排序到第一个,依此类推。
编辑:这些对象不是ActiveRecord
对象
我有一个place
具有以下参数的对象:phone
, category
, street
, zip
, website
.
我还有一个place
对象数组:[place1, place2, place3, place4, place5]
.
place
根据参数可用性对 s 数组进行排序的最佳方法是什么?即,如果place1
有最多可用的参数,或最少的参数nil
,它应该重新排序到第一个,依此类推。
编辑:这些对象不是ActiveRecord
对象
我会让每个 Place 对象知道它有多完整:
class Place
attr_accessor :phone, :category, :street, :website, :zip
def completeness
attributes.count{|_,value| value.present?}
end
end
然后很容易按完整性对您的地点对象进行排序:
places.sort_by(&:completeness)
编辑:非 ActiveRecord 解决方案:
由于Ruby on Rails标签,我曾假设这是一个 ActiveRecord 模型。由于这是一个非 ActiveRecord 模型,因此您可以instance_variables
使用attributes
. (顺便说一句,恭喜你知道 Rails 中的领域模型不必从 ActiveRecord 继承)
class Place
attr_accessor :phone, :category, :street, :website, :zip
def completeness
instance_variables.count{|v| instance_variable_get(v).present?}
end
end
编辑 2:加权属性
您对计算加权分数有意见。在这种情况下,或者当您想要选择特定属性时,您可以在模型中添加以下内容:
ATTR_WEIGHTS = {phone:1, category:1, street:2, website:1, zip:2}
def completeness
ATTR_WEIGHTS.select{|k,v| instance_variable_get(k).present?}.sum(&:last)
end
请注意, thesum(&:last)
等价于sum{|k,v| v}
which 又是reduce(0){|sum, (k,v)| sum += v}
.
如果你有一堂课Place
:
class Place
attr_accessor :phone, :category, :street, :website, :zip
end
然后创建一个实例place1
:
place1 = Place.new
place1.instance_variables # => []
place1.instance_variables.size # => 0
place1.phone = '555-1212' # => "555-1212"
place1.instance_variables # => [ :@phone ]
place1.instance_variables.size # => 1
并创建下一个实例:
place2 = Place.new
place2.phone = '555-1212'
place2.zip = '00000'
place2.instance_variables # => [ :@phone, :@zip ]
place2.instance_variables.size # => 2
您可以按已设置的实例变量的升序排序:
[place1, place2].sort_by{ |p| p.instance_variables.size }
# => [ #<Place:0x007fa8a32b51a8 @phone="555-1212">, #<Place:0x007fa8a31f5380 @phone="555-1212", @zip="00000"> ]
或按降序排序:
[place1, place2].sort_by{ |p| p.instance_variables.size }.reverse
# => [ #<Place:0x007fa8a31f5380 @phone="555-1212", @zip="00000">, #<Place:0x007fa8a32b51a8 @phone="555-1212"> ]
这使用基本的 Ruby 对象,不需要 Rails,它会询问对象实例本身设置了什么,因此您不必维护任何外部属性列表。
注意:如果您将实例变量设置为某个值,然后将其设置回nil
.
这修复了它:
[place1,place2].sort_by{ |p|
p.instance_variables.reject{ |v|
p.instance_variable_get(v).nil?
}.size
}.reverse
这通过使用count
带有块的 Enumerable 来缩短它:
[place1,place2].sort_by{ |p|
p.instance_variables.count{ |v|
!p.instance_variable_get(v).nil?
}
}.reverse
我确信有更好的方法,但这是一个开始:
values = {phone: 5, category: 3, street: 5, website: 3, zip: 5} #Edit these values to ponderate.
array = [place1, place2, place3, place4, place5]
sorted_array = array.sort_by{ |b| b.attributes.select{ |k, v| values.keys.include?(k.to_sym) && v.present? }.inject(0){ |sum, n| sum + values[n[0]] } }.reverse
因此,我们基本上是attributes
通过仅选择散列中的键值对values
并且仅当它们具有present?
值时才创建 ActiveRecord 对象的子散列。
然后在这个子散列上,我们调用inject
它将对我们放入values
散列中的计算值求和。最后,我们反转所有内容,以便您首先获得最高分。
为了使它干净,我建议您实现一个方法,该方法将计算模型中实例方法中每个对象的分数,如标记建议