0

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.

9
  • Why did imp 1 and imp 4 get put into one object? What's the logic around that. Commented Mar 20, 2017 at 21:36
  • Actually i want to make it DRY. So i m merging 1,2,3,4 and making range 1-4 Commented Mar 20, 2017 at 22:00
  • only imp and older_serial are concatenated? What about old_num and acceptable_nums Commented Mar 20, 2017 at 22:05
  • @Anthony: They're the same. Commented Mar 20, 2017 at 22:05
  • 1
    For reasons that should be obvious, questions are not to be changed after answers have been posted. Also, when you give an example, all inputs should be valid Ruby objects (as you had before), so no [1], [2], etc , and a variable should be assigned to each. That allows readers to cut-and-paste and reference the variables without having to define then Commented Mar 20, 2017 at 23:11

3 Answers 3

1

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" 
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you. Will this works if the hashes are the active record object? because i am getting NoMethodError: undefined method values_at' for ` #<Subsitute:0x0000000901fd70>
@Kavincat: Before you write hash in the title, in your question, in your variables and in your data structure, it might be a good idea to make sure you're really working with hashes.
@Kavincat: you might want to try : .group_by{ |substitute| [substitute.acceptable_nums, substitute.old_num]} But it's hard to say since you mention ActiveRecord for the first time in your comment.
1
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"]} 

1 Comment

Plot twist : apparently, array_of_hash isn't an array of hashes. :-/
0

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} 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.