Tuara
Tuara

Reputation: 11

Ruby - Sort multidimensional array by first line

I need to sort a multidimensional array by first line. The first line (array[0][0] to array[0][n-1]) is composed of strings, I need to sort it, and the other lines to follow up...

I already searched a lot and saw how to use the sort function to sort it by column, but didn't found out how to apply it to my problem...
I already solved my problem with a bubble sort who sort the first line, then report the change to the other lines if there's one, but I wondered if there was a better way to do it ?

Creation of the array : array = Array.new(4) { Array.new(var, 0) }

I have something like that in it :

[ [ "A 1", "A 3", "A 2", "A 4" ],
[ 4, 5, 6, 7 ],
[ 2, 2, 2, 2 ],
[ 0.1, 0.2, 0.1, 0.2 ] ]

The expected result would be as followed :

[ [ "A 1", "A 2", "A 3", "A 4" ],
[ 4, 6, 5, 7 ],
[ 2, 2, 2, 2 ],
[ 0.1, 0.1, 0.2, 0.2 ] ]

Upvotes: 0

Views: 486

Answers (2)

Marcin Kołodziej
Marcin Kołodziej

Reputation: 5313

Less Ruby'ish approach:

sorted = arr.first.sort
# => ["A 1", "A 2", "A 3", "A 4"] 
order_arr = arr.first.map { |x| sorted.index(x) }
#  => [0, 2, 1, 3]
arr.map { |a| order_arr.map { |x| a[x] } }
# => [["A 1", "A 2", "A 3", "A 4"],
#     [4, 6, 5, 7],
#     [2, 2, 2, 2],
#     [0.1, 0.1, 0.2, 0.2]]

Upvotes: 0

engineersmnky
engineersmnky

Reputation: 29318

You can use Array#transpose and Enumerable#sort_by to handle this like so:

 arr = [ [ "A 1", "A 3", "A 2", "A 4" ],
         [ 4, 5, 6, 7 ],
         [ 2, 2, 2, 2 ],
         [ 0.1, 0.2, 0.1, 0.2 ] ]

Array#transpose turns rows into columns:

arr.transpose
#=> [["A 1", 4, 2, 0.1],
#    ["A 3", 5, 2, 0.2],
#    ["A 2", 6, 2, 0.1],
#    ["A 4", 7, 2, 0.2]]

Then we just need to sort by the first column values sort_by(&:first):

arr.transpose.sort_by(&:first)
#=> [["A 1", 4, 2, 0.1],
#    ["A 2", 6, 2, 0.1],
#    ["A 3", 5, 2, 0.2],
#    ["A 4", 7, 2, 0.2]]

Then we just transpose back again:

arr.transpose.sort_by(&:first).transpose
#=> [["A 1", "A 2", "A 3", "A 4"],
#    [4, 6, 5, 7],
#    [2, 2, 2, 2],
#    [0.1, 0.1, 0.2, 0.2]]

The same could be achieved by zipping the Arrays together like so: (but the former seems like a better choice)

arr.reduce(&:zip).sort_by {|a| a.flatten!.first}.transpose
#=> [["A 1", "A 2", "A 3", "A 4"],
#    [4, 6, 5, 7],
#    [2, 2, 2, 2],
#    [0.1, 0.1, 0.2, 0.2]]

Upvotes: 2

Related Questions