Reputation: 390
I have an array of hash that needs to be sorted by imp
and I want to get the specific attribute out of it.
array_of_hash is
[
{
:id => "9",
:subsystem => "xyz",
:component => "xyz",
:imp => "1",
:old_serial => "55q",
:current_serial => nil,
:old_num => "same",
:current_num => nil,
:acceptable_nums => [
"asdf",
"qwer",
"zxcv",
"poiu"
]
},
{
:id => "10",
:subsystem => "xyz",
:component => "xyz",
:imp => "4",
:old_serial => "56t",
:current_serial => nil,
:old_num => "same",
:current_num => nil,
:acceptable_nums => [
"asdf",
"qwer",
"zxcv",
"poiu"
]
},
{
:id => "11",
:subsystem => "xyz",
:component => "xyz",
:imp => "3",
:old_serial => "57s",
:current_serial => nil,
:old_num => "same",
:current_num => nil,
:acceptable_nums => [
"asdf",
"qwer",
"zxcv",
"poiu"
]
},
{
:id => "14",
:subsystem => "xyz",
:component => "xyz",
:imp => "2",
:old_serial => "58r",
:current_serial => nil,
:old_num => "same",
:current_num => nil,
:acceptable_nums => [
"asdf",
"qwer",
"zxcv",
"poiu"
]
}
]
First step, sorting
array_of_hash.sort_by {|hash| hash[:imp].to_i}
Then i want specific attribute
Desired output with some condition
{
:imp => "1-4", #It should be range
:old_serial => "55q,56r,57s,58t", #old_serial number should be separated with comma respectively
:old_num => "same",
:acceptable_nums => [
"asdf",
"qwer",
"zxcv",
"poiu"
]
}
I am not able to figure out how to do this.
Upvotes: 0
Views: 3003
Reputation: 110675
imps, old_serials = array_of_hash.map { |h| [h[:imp], h[:old_serial]] }.
sort_by(&:first).
transpose
#=> [["1", "2", "3", "4"], ["55q", "58r", "57s", "56t"]]
{ imp: "%d-%d" % imps.map(&:to_i).minmax, old_serial: old_serials.join(',') }.
merge(array_of_hash.first.select { |k,_| [:old_num, :acceptable_nums].include?(k) })
#=> {:imp=>"1-4", :old_serial=>"55q,58r,57s,56t", :old_num=>"same",
# :acceptable_nums=>["asdf", "qwer", "zxcv", "poiu"]}
Note
array_of_hash.first.select { |k,_| [:old_num, :acceptable_nums].include?(k) }
# => {:old_num=>"same", :acceptable_nums=>["asdf", "qwer", "zxcv", "poiu"]}
Upvotes: 1
Reputation: 1873
It's a bit easier to read if you break this up in steps.
old_serial_numbers = array_of_hash.collect {|h| h[:old_serial]}.join(',')
reject_hash_keys = [:old_serial, :id, :subsystem, :component, :current_num, :current_serial]
clean_hash = array_of_hash.map do |hash|
hash.reject! {|k| reject_hash_keys.include? k }
hash.merge(old_serial: old_serial_numbers)
end
clean_hash.sort_by {|hash| hash[:imp].to_i}
Upvotes: 0
Reputation: 54223
You'll need a combination of sort_by
, group_by
and map
:
p array_of_hash.sort_by { |h| h[:imp] }
.group_by{ |h| h.values_at(:acceptable_nums, :old_num) }
.map{ |(old_num, nums), hashes|
{
imp: hashes.map{ |h| h[:imp].to_i },
old_serial: hashes.map{ |h| h[:old_serial] }.join(','),
old_num: old_num,
acceptable_nums: nums
}
}
# [{:imp=>[1, 2, 3, 4], :old_serial=>"55q,58r,57s,56t", :old_num=>["asdf", "qwer", "zxcv", "poiu"], :acceptable_nums=>"same"}]
The output is an array of hashes. There will be one hash for each unique pair of old_num
and acceptable_nums
. In your example, all the hashes had this same pair, so only one hash is outputted.
As for the desired conversion from [1,2,3,4]
to "1-4"
, the documentation for slice_when
does just that :
a = [1,2,4,9,10,11,12,15,16,19,20,21]
b = a.slice_when {|i, j| i+1 != j }
p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" }
p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"]
d = c.join(",")
p d #=> "1,2,4,9-12,15,16,19-21"
Upvotes: 1