2016-07-27 2 views
0

У меня есть массив «размеры», которые выглядят следующим образом: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?

+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

сопоставимый Mixin используется классов, объекты могут быть заказаны. Класс должен определить оператор < =>, который сравнивает приемник с другим объектом, возвращающим -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

Этот подход включает в себя больше шагов, чем те, которые используют sort или sort_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">] 
+0

вау, спасибо Cary! Для этого случая я придерживаюсь решения sort_by, но для более крупных массивов я выберу ваш. Удивительное объяснение кстати. – StandardNerd

Смежные вопросы