2016-09-20 3 views
2

Я новичок в Ruby и хочу сортировать только определенные предметы в моей коллекции. Например, если у меня есть следующий массив. Я только хочу, чтобы отсортировать объекты, которые содержат свойство type: 'sort'Сортировка определенных объектов в массиве

object = [{ 
      type: 'sort', 
      id: 3 
     }, { 
      type: 'notsort', 
      id: 4 
     }, { 
      type: 'sort', 
      id: 1 
     }, { 
      type: 'sort', 
      id: 0 
     } 
    ] 

Мне нужен порядок для отображения непосредственно на карту ид ниже.

sortIdOrder = [0, 1, 3] 

Конечный результат должен выглядеть следующим образом:

object = [{ 
    type: 'notsort', 
    id: 4 
}, { 
    type: 'sort', 
    id: 0 
},{ 
    type: 'sort', 
    id: 1 
}, { 
    type: 'sort', 
    id: 3 
}] 

Как вы можете видеть массив отсортирован по id на основе sortIdOrder. notsorttype может быть либо в конце, либо в начале.

+0

В качестве примечания, соглашение содержит, что методы и переменные Ruby должны иметь вид 'sort_id_order'. – tadman

ответ

0

не очень производительным один вкладыш:

object.sort_by{|o| sortIdOrder.index(o[:id]) || -1} 

Это делает notsort объекты появляются во главе отсортированного массива. Это алгоритм O(m * nlog(n)), где n - размер object и m - размер sortIdOrder. Это быстрее, когда ваши object и sortIdOrder являются небольшими.

Более производительные один для больших массивов

order = sortIdOrder.each.with_index.with_object(Hash.new(-1)) {|(id, index), h| h[id] = index} 

object.sort_by{|o| order[o[:id]]} 

Это O (M +) Nlog (п) алгоритм, но требует больше памяти.

0

Вы можете использовать sort с блоком, который сортирует по :type, затем :id.

object.sort {|a, b| [a[:type], a[:id]] <=> [b[:type], b[:id]] } 

[{:type=>"notsort", :id=>4}, 
{:type=>"sort", :id=>0}, 
{:type=>"sort", :id=>1}, 
{:type=>"sort", :id=>3}] 
+0

Спасибо, но я бы хотел, чтобы порядок определялся 'sortIdOrder'. Я бы просто изменил решение на: 'object.sort {| a, b | [a [: type], sortIdOrder.index (a [: id])] <=> [b [: type], sortIdOrder.index (b [: id])]} '? – Decrypter

+0

Да, это работает. По крайней мере, для этого примера. – davidhu2000

+0

Прохладный. Однако некоторые типы 'type' могут быть ноль. Не будет ли это работать в этом случае? – Decrypter

0

Я бы с чем-то вроде этого:

object.sort_by do |o| 
    [ 
    (o[:type] == :sort) ? 0 : 1, 
    sortIdOrder.index(o[:id]) 
    ] 
end 

При сортировке по массиву, вы по существу сортировку по первому элементу, кроме случаев, когда они одинаковы, и в этом случае вы сортируете по второму элементу и т. д. В приведенном выше коде (o[:type] == :sort) ? 0 : 1 гарантирует, что все с типом :sort на первом месте, и все остальное после, даже если тип nil или 5 или что угодно. Термин sortIdOrder.index(o[:id]) гарантирует, что вещи отсортированы по своему усмотрению (хотя элементы без :id, или чьи :id не найдены в sortIdOrder, будут упорядочены произвольно. Если ваш набор данных очень велик, вы можете настроить его далее, чтобы массив sortIdOrder не выполняется для элементов, не относящихся к сортировке.

Enumerable#sort_by только должен вызывать блок один раз для каждого элемента, а затем выполнять быстрые сравнения результатов: Enumerable#sort должен вызывать блок по парам элементов, что означает, что он называется более часто:

irb(main):015:0> ary = %w{9 8 7 6 5 4 3 2 1} 
=> ["9", "8", "7", "6", "5", "4", "3", "2", "1"] 
irb(main):016:0> a = 0; ary.sort_by {|x| puts x; a+=1; x.to_i }; puts "Total: #{a}" 
9 
8 
7 
6 
5 
4 
3 
2 
1 
Total: 9 
=> nil 
irb(main):017:0> a = 0; ary.sort {|x,y| puts "#{x},#{y}"; a+=1; x.to_i <=> y.to_i }; puts "Total: #{a}" 
9,5 
5,1 
8,5 
2,5 
7,5 
3,5 
6,5 
4,5 
6,8 
8,9 
7,8 
6,7 
1,3 
3,4 
2,3 
1,2 
Total: 16 
=> nil 

В этих случаях это действительно не очень важно, потому что хеш-доступ в любом случае быстро (хотя sort_byеще более разборчиво), но в тех случаях, когда вычисление атрибута, которое вы хотите отсортировать, даже умеренно дорого, sort_by может быть довольно быстрым. Блочная форма sort в основном полезна, если сама логика сравнения сложна.

+0

Спасибо за ответ. Что делать, если ': type' равен нулю? – Decrypter

+0

Если ': type' может быть nil, или если у вас есть много разных типов, кроме: sort и: nosort, и вы хотите быть уверенным, что все, кроме сортировки, сгруппированы вместе, вам будет лучше использовать' [(o [ : type] ==: sort? 0: 1), sortIdOrder.index (o [: id]) 'как ваш критерий сортировки. Это поместит все ': sort' в начале списка (в желаемом порядке сортировки) и все остальное после (в потенциально произвольном порядке, если их идентификаторы отсутствуют в массиве' sortIdOrder'. обновите ответ, чтобы это отразить. – philomory

3

Сортировка может быть дорогостоящей, поэтому не следует сортировать, когда желаемый порядок известен, как он есть здесь.

Я предположил, что значения :id уникальны, так как вопрос не имеет смысла, если бы они не были.

Сначала разделите хэши в те, которые будут отсортированы, а остальное.

sortees, nonsortees = object.partition { |h| h[:type] == 'sort' } 
    #=> [[{:type=>"sort", :id=>3}, {:type=>"sort", :id=>1}, {:type=>"sort", :id=>0}], 
    # [{:type=>"notsort", :id=>4}]] 

так

sortees 
    #=> [{:type=>"sort", :id=>3}, {:type=>"sort", :id=>1}, {:type=>"sort", :id=>0}] 
nonsortees 
    #=> [{:type=>"notsort", :id=>4}] 

Я положу элементы sortees в нужном порядке, то сцепить этот массив с nonsortees, помещая хэши, которые не должны быть отсортированы в конце.

я заказать элементы sortees путем создания хэш с одним ключом пары значений g[:id]=>g для каждого элемента g (а хэш) sortees. Это позволяет мне использовать Hash#values_at, чтобы вытащить нужные хэши в указанном порядке.

sortees.each_with_object({}) { |g,h| h[g[:id]] = g }. 
     values_at(*sortIdOrder). 
     concat(nonsortees) 
    #=> [{:type=>"sort", :id=>0}, {:type=>"sort", :id=>1}, {:type=>"sort", :id=>3}, 
    # {:type=>"notsort", :id=>4}] 

Обратите внимание, что

sortees.each_with_object({}) { |g,h| h[g[:id]] = g } 
    #=> {3=>{:type=>"sort", :id=>3}, 1=>{:type=>"sort", :id=>1}, 
    # 0=>{:type=>"sort", :id=>0}} 
+0

Вы знаете, 'sort_by' с вызовом' index_of' может быть здесь.Для более крупных списков вам обязательно нужно преобразовать массив порядка ID в хэш: 'Hash [array.each_with_index.to_a] .invert' может сделать это легко. – tadman

+0

@tadman, это приятное использование 'each_with_index' и' invert'. Я должен убрать это. –

0

Может быть, я опоздал, но мое решение:

Rails Решение:

object.partition { |hash| hash[:id].in?(sortIdOrder) }.flatten.reverse 

рубин Решение:

object.partition { |hash| sortIdOrder.include? hash[:id] }.flatten.reverse 

оба его в результате дать этому :

=> [{:type=>"notsort", :id=>4}, 
    {:type=>"sort", :id=>0}, 
    {:type=>"sort", :id=>1}, 
    {:type=>"sort", :id=>3}] 
Смежные вопросы