Ger Cas
Ger Cas

Reputation: 2298

Custom sorting array by groups with Ruby

Please your help. I have the input_array with 3 groups of values.

The group order I need is:

1st group: Pou
2nd group: Apl
3rd group: Gab

This order is already done in input_array, but I want to sort ascending each group.

The script I have so far produces the output given in central column of table below (current output).

input_arr = ["Pou-12","Pou-7","Pou-4","Pou-8","Pou-9","Pou-11","Pou-10","Pou-3","Pou-2","Pou-1","Pou-6","Pou-5","Apl","Gab-3","Gab-5","Gab-4","Gab-1","Gab-2"] 

b=input_arr.sort_by do |s|
  if s =~ /^\d+$/
      [2, $&.to_i]
  else
      [1, s]
  end
end

puts b

and the desired sorted output array is shown below too.


 input array  | current output | desired output array

    Pou-12    |     Apl        |      Pou-1
    Pou-7     |     Gab-1      |      Pou-2
    Pou-4     |     Gab-2      |      Pou-3
    Pou-8     |     Gab-3      |      Pou-4
    Pou-9     |     Gab-4      |      Pou-5
    Pou-11    |     Gab-5      |      Pou-6
    Pou-10    |     Pou-1      |      Pou-7
    Pou-3     |     Pou-10     |      Pou-8
    Pou-2     |     Pou-11     |      Pou-9
    Pou-1     |     Pou-12     |      Pou-10
    Pou-6     |     Pou-2      |      Pou-11
    Pou-5     |     Pou-3      |      Pou-12
    Apl       |     Pou-4      |      Apl
    Gab-3     |     Pou-5      |      Gab-1
    Gab-5     |     Pou-6      |      Gab-2
    Gab-4     |     Pou-7      |      Gab-3
    Gab-1     |     Pou-8      |      Gab-4
    Gab-2     |     Pou-9      |      Gab-5

UPDATE

Cary's solution with array instead of hash.

a = ["Pou-7","Pou-4","Gab-4","Pou-8","Pou-9","Pou-11","Pou-10","Pou-3","Pou-2","Pou-1","Pou-6","Pou-5","Apl","Gab-3","Gab-5","Gab-1","Pou-12","Gab-2"]
order = [ "Pou", "Apl", "Gab" ]

a.map{ |s| head,tail = s.split("-"); [order.index(head), tail.to_i, s]}.sort.map(&:last)

=> ["Pou-1", "Pou-2", "Pou-3", "Pou-4", "Pou-5", "Pou-6", "Pou-7", "Pou-8", "Pou-9", 
"Pou-10", "Pou-11", "Pou-12", "Apl", "Gab-1", "Gab-2", "Gab-3", "Gab-4", "Gab-5"]

Upvotes: 2

Views: 885

Answers (4)

Cary Swoveland
Cary Swoveland

Reputation: 110725

a = ["Pou-12","Pou-7","Pou-4","Pou-8","Pou-9","Pou-11","Pou-10","Pou-3","Pou-2",
     "Pou-1","Pou-6","Pou-5","Apl","Gab-3","Gab-5","Gab-4","Gab-1","Gab-2"]

order = { "Pou"=>0, "Apl"=>1, "Gab"=>2 }
a.map do |s|
  head, tail = s.split('-')
  [order[head], tail.to_i, s]
end.sort.map(&:last)
  #=> ["Pou-1", "Pou-2", "Pou-3", "Pou-4", "Pou-5", "Pou-6", "Pou-7", "Pou-8", "Pou-9",
  #    "Pou-10", "Pou-11", "Pou-12", "Apl, "Gab-1", "Gab-2", "Gab-3", "Gab-4", "Gab-5"]

Note that head, tail = "Apl".split('-') #=> ["Apl"], so head #=> "Apl" and tail #=> nil, but then tail.to_i #=> nil.to_i #=> 0.

See Array#<=> to understand how arrays are compared when sorting.

Upvotes: 2

Ho Man
Ho Man

Reputation: 2345

You can try this:

b = input_arr.group_by { |e| e.split("-")[0] }
b = b.flat_map { |k, v| v.sort_by!{ |vs| vs.split("-")[1].to_i }; v }

Upvotes: 2

Abhi
Abhi

Reputation: 4261

Your initial array looks like:

a = ["Pou-12","Pou-7","Pou-4","Pou-8","Pou-9","Pou-11","Pou-10","Pou-3","Pou-2","Pou-1","Pou-6","Pou-5","Apl","Gab-3","Gab-5","Gab-4","Gab-1","Gab-2"]

d = []

Group the array by it's first part:

c = a.group_by{|b| b.split("-").first}
#=> {"Pou"=>["Pou-12", "Pou-7", "Pou-4", "Pou-8", "Pou-9", "Pou-11",
#=>          "Pou-10", "Pou-3", "Pou-2", "Pou-1", "Pou-6", "Pou-5"],
#=>  "Apl"=>["Apl"],
#=>  "Gab"=>["Gab-3", "Gab-5", "Gab-4", "Gab-1", "Gab-2"]
#=> }

Now, sort the grouped array individually:

c.each do |k, v|
  d << v.sort{|b, c| ( b.split("-").last.to_i <=> c.split("-").last.to_i )}
end

Finally, since the output is Array of Array, you flatten it:

d.flatten!

#=> ["Pou-1", "Pou-2", "Pou-3", "Pou-4", "Pou-5", "Pou-6", "Pou-7", "Pou-8", "Pou-9", "Pou-10", "Pou-11", "Pou-12", "Apl", "Gab-1", "Gab-2", "Gab-3", "Gab-4", "Gab-5"]

Upvotes: 1

djaszczurowski
djaszczurowski

Reputation: 4523

input = ["Pou-12","Pou-7","Pou-4","Pou-8","Pou-9","Pou-11","Pou-10","Pou-3","Pou-2","Pou-1","Pou-6","Pou-5","Apl","Gab-3","Gab-5","Gab-4","Gab-1","Gab-2"]      

grouped_by_prefix = input.group_by do |item| 
  item.split('-')[0] 
end

grouped_by_prefix.each do |_, values| 
  values.sort_by! { |value| value.split('-')[1].to_i } 
end

['Pou', 'Apl', 'Gab'].reduce([]) do |memo, key| 
  memo + (grouped_by_prefix[key] || []) 
end

algorithm is not super efficient (for example double split operation) but quite simple to understand

Upvotes: 2

Related Questions