3

Please your help. I have the array "values" that I want to sort by 2 conditions.

First condition: Using the sort list defined in array "sortlist". Second condition: From smallest to largest.

With my current script I've been able to print the values in correct order for first condition (sort list), but I don't have an idea how to apply the second sorting condition (smallest to largest).

ruby -e'
values = [ "Ghu_1","Prw_1","Prw_3","Prw_5","Vep_3","Hom_2","Vep_1","Hom_1","Prw_2","Vep_2","Prw_4" ]
sortlist = [ "Hom","Vep","Ghu","Prw" ]

sortlist.each{ |s| 
 values.each{ |v| 
     puts v if v.include?(s)
 }
}'




Current Ouput  #  Desired output
   Hom_2       #      Hom_1
   Hom_1       #      Hom_2
   Vep_3       #      Vep_1
   Vep_1       #      Vep_2
   Vep_2       #      Vep_3
   Ghu_1       #      Ghu_1
   Prw_1       #      Prw_1
   Prw_3       #      Prw_2
   Prw_5       #      Prw_3
   Prw_2       #      Prw_4
   Prw_4       #      Prw_5

UPDATE

Thank Sebastian. Excellent.

It almost work since I noticed that if the characters after "_" are not numbers the second sort is not correct. For example below output is incorrect for values of Pwr_XXX

 ruby -e'
 values = [ "Ghu_Klca","Prw_Rkdg","Prw_Ceteu","Prw_Eriir","Vep_Msas23","Hom_Ttgr5","Vep_Qsccas","Hom_Ftjh","Prw_jpolq","Vep_Szbqh","Prw_Lmnajh" ]
 sortlist = [ "Hom","Vep","Ghu","Prw" ]

 puts sortlist.flat_map{ |s|
   values.select{ |v|
     v if v.include?(s)
   }.sort
 }'

2nd Update

I mean that the first sort is based on sortlist array. The second sort is ascending based in characters after "_". In this case the firt letter for Prw values are C, E, j, L and R. But the current output is C, E, L, R and j. So, j is at the end and should be after L. I hope make sense.

Current output        Expected output
Hom_Ftjh          #      Hom_Ftjh 
Hom_Ttgr5         #      Hom_Ttgr5 
Vep_Msas23        #      Vep_Msas23 
Vep_Qsccas        #      Vep_Qsccas 
Vep_Szbqh         #      Vep_Szbqh 
Ghu_Klca          #      Ghu_Klca 
Prw_Ceteu         #      Prw_Ceteu 
Prw_Eriir         #      Prw_Eriir 
Prw_Lmnajh        #      Prw_jpolq 
Prw_Rkdg          #      Prw_Lmnajh 
Prw_jpolq         #      Prw_Rkdg
Ger Cas
  • 2,188
  • 2
  • 18
  • 45

2 Answers2

2

Looking at your code, what you could do is to create an empty array, and then inside your values.each iteration, to push each element to your empty array if that element include the element from the sortlist

new_list = []
sortlist.each{ |s|
  values.each{ |v|
    new_list << v if v.include?(s)
  }
}

Then use group_by to group them by its first character:

p new_list.group_by{|e| e[0]}
# => {"H"=>["Hom_2", "Hom_1"], "V"=>["Vep_3", "Vep_1", "Vep_2"], "G"=>["Ghu_1"], "P"=>["Prw_1", "Prw_3", "Prw_5", "Prw_2", "Prw_4"]}

Then use flat_map to get a "plain" array with all your values, but inside the iteration sort each values for each key in the hash:

p new_list.group_by{|e| e[0]}.flat_map{|_,v| v.sort}
# => ["Hom_1", "Hom_2", "Vep_1", "Vep_2", "Vep_3", "Ghu_1", "Prw_1", "Prw_2", "Prw_3", "Prw_4", "Prw_5"]

Or a flat_map, select and sort_by combination (2nd update):

values = [ "Ghu_Klca","Prw_Rkdg","Prw_Ceteu","Prw_Eriir","Vep_Msas23","Hom_Ttgr5","Vep_Qsccas","Hom_Ftjh","Prw_jpolq","Vep_Szbqh","Prw_Lmnajh" ]
sortlist = [ "Hom","Vep","Ghu","Prw" ]

p sortlist.flat_map{ |s|
  values.select{ |v|
    v if v.include?(s)
  }.sort_by(&:downcase)
}
# => ["Hom_Ftjh", "Hom_Ttgr5", "Vep_Msas23", "Vep_Qsccas", "Vep_Szbqh", "Ghu_Klca", "Prw_Ceteu", "Prw_Eriir", "Prw_jpolq", "Prw_Lmnajh", "Prw_Rkdg"]

Use sort_by(&:downcase) to "ignore" case when sorting.

Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
  • Just work exactly like I was looking for. Your 2nd update even sorts numeric values! Muchas Gracias Sebastián. – Ger Cas Jul 20 '17 at 02:17
2
values.sort_by { |val|
  front, back = val.split('_')
  [sortlist.index(front), back.downcase]
}
# => ["Hom_1", "Hom_2", "Vep_1", "Vep_2", "Vep_3", "Ghu_1", "Prw_1", "Prw_2", "Prw_3", "Prw_4", "Prw_5"]
# or
# => ["Hom_Ftjh", "Hom_Ttgr5", "Vep_Msas23", "Vep_Qsccas", "Vep_Szbqh", "Ghu_Klca", "Prw_Ceteu", "Prw_Eriir", "Prw_Lmnajh", "Prw_Rkdg", "Prw_jpolq"]

This works because arrays compare by comparing the first element, then comparing the next element if the previous is equal. Currently I'm comparing the back as if it were a string (10 comes before 2); if you want numeric ordering (2 comes before 10), use back.to_i or back.to_f (but that won't work with the second example).

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thanks Amadan for your explanation. Do you know how could be fixed the sorting within each group of similar values? I call it the second sort. – Ger Cas Jul 20 '17 at 00:14
  • Based on your clarification, you want case-insensitive sort. This can be done by a `downcase` in the sorting criterion. Answer changed accordingly. – Amadan Jul 20 '17 at 10:14
  • Thanks so much Amadan. It works just fine your updated solution. – Ger Cas Jul 20 '17 at 18:41