Ruby:在名称属性数组之后对对象数组进行排序

时间:2016-07-27 12:07:45

标签: ruby

我有一个数组“大小”,如下所示:

[#<OPTIONVALUE ID: 5, NAME: "M">,
#<OPTIONVALUE ID: 6, NAME: "M/L">, 
#<OPTIONVALUE ID: 7, NAME: "XS/S">]

考虑属性NAME的值。数组排序:M,M / L,XS / S.

但排序顺序应如下所示:

@sizes_sort_order = ["XS", "XS/S", "S", "S/M", "M", "M/L", "L", "L/XL", "XL"]

应用于前一个数组,元素的顺序应如下所示:

[#<SPREE::OPTIONVALUE ID: 7, NAME: "XS/S">,
#<SPREE::OPTIONVALUE ID: 5, NAME: "M">,
#<SPREE::OPTIONVALUE ID: 6, NAME: "M/L">]

def sizes
  @sizes ||= grouped_option_values_by_option_type[Spree::OptionType.find_by!(name: 'size')]
  @sizes_sort_order = ["XS", "XS/S", "S", "S/M", "M", "M/L", "L", "L/XL", "XL"]
  @sizes.map { # sort after @size_sort_order }
end

如何在@sizes_sort_order之后将数组中的元素排序?

3 个答案:

答案 0 :(得分:3)

您可以使用Enumerable#sort_by

my_array.sort_by {|x| @sizes_sort_order.index(x.name) }

答案 1 :(得分:2)

您可以包含Comparable模块以获得对象的自然排序。

http://ruby-doc.org/core-2.2.3/Comparable.html

  

Comparable mixin由可以对其对象进行排序的类使用。   该类必须定义&lt; =&gt;运算符,用于比较接收器   对另一个对象,返回-1,0或+1取决于是否   接收器小于,等于或大于另一个物体。

class Size
  include Comparable

  SIZES = ["XS", "XS/S", "S", "S/M", "M", "M/L", "L", "L/XL", "XL"]

  attr_reader :name
  def initialize(id, name)
    @id = id
    @name = name
  end

  def <=>(b)
    SIZES.index(name) <=> SIZES.index(b.name)
  end
end

a = Size.new(5, 'M')
b = Size.new(6, 'M/L')
c = Size.new(7, 'XS/S')

print [a, b, c].sort

[#<Size:0x007f8e910458e0 @id=7, @name="XS/S">, #<Size:0x007f8e910459a8 @id=5, @name="M">, #<Size:0x007f8e91045930 @id=6, @name="M/L">]

答案 2 :(得分:1)

这种方法涉及的步骤多于使用sortsort_by的方法,但对于较大的数组,它可能更快,因为不涉及排序 - 这是相对昂贵的。

<强>代码

def reorder_by_size(instances, size_order)
  instances.each_with_object({}) { |inst, h| h.update(inst.name=>inst) }.
    values_at(*(size_order & (instances.map { |s| s.name })))
end

示例

首先让我们创建一个

的实例数组
class Sizes
  attr_reader :name

  def initialize(id, name)
    @id = id
    @name = name
  end
end
像这样:

instances = [Sizes.new(5,'M'), Sizes.new(6,'M/L'), Sizes.new(7, 'XS/S')]
  #=> [#<Sizes:0x007fa66a955ac0 @id=5, @name="M">,
  #    #<Sizes:0x007fa66a955a70 @id=6, @name="M/L">,
  #    #<Sizes:0x007fa66a955a20 @id=7, @name="XS/S">]

然后

reorder_by_size(instances, @sizes_sort_order)
 #=> [#<Sizes:0x007fa66a01dfc0 @id=7, @name="XS/S">,
 #    #<Sizes:0x007fa66a86fdb8 @id=5, @name="M">,
 #    #<Sizes:0x007fa66a8404f0 @id=6, @name="M/L">] 

<强>解释

对于示例中定义的instances,首先按所需顺序创建一个大小数组:

names = @sizes_sort_order & (instances.map { |s| s.name })
  #=> ["XS/S", "M", "M/L"]

重要Array#&状态的文档,“订单将从原始数组中保留。”

现在我们可以在不进行排序的情况下创建所需的重新排序,方法是创建一个带有键大小和值的实例的哈希,然后使用Hash#values_at以所需的顺序提取实例。

instances.each_with_object({}) { |inst, h|
  h.update(inst.name=>inst) }.values_at(*names)
  #=> [#<Sizes:0x007fa66a01dfc0 @id=7, @name="XS/S">,
  #    #<Sizes:0x007fa66a86fdb8 @id=5, @name="M">,
  #    #<Sizes:0x007fa66a8404f0 @id=6, @name="M/L">] 

最后一项操作涉及以下两个步骤。

h = instances.each_with_object({}) { |inst, h| h.update(inst.name=>inst) }
  #=> {"M" => #<Sizes:0x007fa66a955ac0 @id=5, @name="M">,
  #    "M/L" => #<Sizes:0x007fa66a955a70 @id=6, @name="M/L">,
  #    "XS/S" => #<Sizes:0x007fa66a955a20 @id=7, @name="XS/S">} 
h.values_at(*names)
  #=> h.values_at(*["XS/S", "M", "M/L"])
  #=> h.values_at("XS/S", "M", "M/L")
  #=> [#<Sizes:0x007fa66a955a20 @id=7, @name="XS/S">,
  #    #<Sizes:0x007fa66a955ac0 @id=5, @name="M">,
  #    #<Sizes:0x007fa66a955a70 @id=6, @name="M/L">]