Reputation: 25979
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
Upvotes: 1
Views: 204
Reputation: 95358
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"}]
Upvotes: 2
Reputation: 35541
This should work:
sorted = wardList.sort_by{|a| [a[:seqno] ? 0 : 1, a[:seqno], a[:name]] }
or for some rubies (e.g. 1.8.7):
sorted = wardList.sort_by{|a| [a[:seqno] ? 0 : 1, a[:seqno] || 0, a[:name]] }
Upvotes: 1
Reputation: 54734
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
Upvotes: 0