简体   繁体   中英

Sorting an array of hashes in ruby

I have an array of hashes which look like:

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

All fields are strings.

Now I want to sort them first on seqno and then on name. seqno can be nil (if seqno is nil, then this ward must come after the ones having a seqno).

What I have so far is:

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

But this gives me the error: can't convert Symbol into Integer

First, normalize your data , you can't work with integers as strings here:

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

Then you can use sort_by , which supports lexicographical sorting:

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

Example:

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"}]

This should work:

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

or for some rubies (eg 1.8.7):

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

I don't think you should use return here, it causes the block to return to the iterator, the iterator to return to the enclosing method and the enclosing method to return to its caller. Use next instead which only causes the block to return to the iterator ( sort! in this case) and do something like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM