2016-07-27 45 views
0

我有一个数组“大小”,看起来像这样:红宝石:名称属性数组后的对象排序数组

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

考虑属性名称的值。数组排序: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之后排序的数组中?

+1

看['可枚举#sort_by'(http://ruby-doc.org/core-2.2.0/Enumerable.html#method-i-sort_by) –

回答

2

可以包括Comparable模块获取的对象的自然排序。

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

可比混入由类其对象可以是有序的使用。 该类必须定义< =>运算符,该运算符将接收方 与另一个对象进行比较,返回-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">] 
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 

首先,让我们创建的

​​

实例的数组像这样:

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">] 
+0

哇,非常感谢卡里!对于这种情况,我坚持sort_by解决方案,但对于更大的阵列,我会选择你的。非常好的解释方式。 – StandardNerd