2012-03-21 4 views
1

У меня есть массив хэшей, которые выглядят как:Сортировка массива хэшей в рубин

ward = {id: id, name: record["Externalization"], mnemonic: record["Mnemonic"], 
    seqno: record["SeqNo"]} 

Все поля являются строками.

Теперь я хочу отсортировать их сначала по seqno, а затем по названию. seqno может быть nil (если seqno nil, то эта палата должна прибыть после тех, у кого есть seqno).

То, что я до сих пор:

wardList.sort! do |a,b| 
    return (a[:name] <=> b[:name]) if (a[:seqno].nil? && b[:seqno].nil?) 
    return -1 if a[:seqno].nil? 
    return 1 if b[:seqno].nil? 
    (a[:seqno] <=> b[:seqno]).nonzero? || 
    (a[:name] <=> b[:name]) 
end 

Но это дает мне ошибку: не может преобразовать символ в Integer

ответ

2

Во-первых, нормализовать ваши данные, вы не можете работать с целыми числами в виде строк здесь:

wardList = wardList.map { |x| x.merge({:id => x[:id].to_i, 
             :seqno => x[:seqno].try(:to_i) }) } 

Затем вы можете использовать sort_by, который поддерживает лексикографический сортировкой:

wardList.sort_by! { |x| [x[:seqno] || Float::INFINITY, x[:name]] } 

Пример:

irb(main):034:0> a = [{:seqno=>5, :name=>"xsd"}, 
         {:seqno=>nil, :name=>"foo"}, 
         {:seqno=>nil, :name=>"bar"}, 
         {:seqno=>1, :name=>"meh"}] 
irb(main):033:0> a.sort_by { |x| [x[:seqno] || Float::INFINITY, x[:name]] } 
=> [{:seqno=>1, :name=>"meh"}, 
    {:seqno=>5, :name=>"xsd"}, 
    {:seqno=>nil, :name=>"bar"}, 
    {:seqno=>nil, :name=>"foo"}] 
+0

Да, но что, если [: seqno] nil? –

+0

Я получаю ошибку без имплицированного преобразования от нуля до целого. –

+0

@Lieven: Проверьте изменения. –

1

Это должно работать:

sorted = wardList.sort_by{|a| [a[:seqno] ? 0 : 1, a[:seqno], a[:name]] } 

или для некоторых рубинов (например, 1.8.7):

sorted = wardList.sort_by{|a| [a[:seqno] ? 0 : 1, a[:seqno] || 0, a[:name]] } 
+0

Да, но он также должен быть отсортирован по имени, если SEQNO равны или оба ноля .. . –

+0

Отредактировано, что должно включать в себя то, что вам нужно – PinnyM

+0

Можете ли вы объяснить, как это работает? –

0

Я не думаю, что вы должны использовать return здесь, это заставляет блок возвращаться к итератору, итератору, чтобы возвратиться к методу окружения и метод включения, чтобы вернуться к его вызывающему. Используйте next вместо, который вызывает только блок для возврата итератора (sort! в данном случае) и сделать что-то вроде:

wardList.sort! do |x,y| 
    next 1 if x[:seqno].nil? 
    next -1 if y[:seqno].nil? 
    comp = x[:seqno] <=> y[:seqno] 
    comp.zero? ? x[:name] <=> y[:name] : comp 
end 
+0

Это дает неправильный результат, если 'x [: seqno] .nil? &&! y [: seqno] .nil? ' –

+0

работает для меня ... можете ли вы дать минимальный пример? –

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