简体   繁体   中英

Sorting an array of arrays in Ruby

I have an array of arrays like so:

irb(main):028:0> device_array
=> [["name1", "type1", ["A", "N", "N"], ["Attribute", "device_attribute"], 9], ["name2","type2", ["A", "N", "N"], ["Attribute", "device_attribute"], 7]]

I would like to sort the entire device_array on the 4th element.

I've tried

AllDevicesController.all_devices.sort do | a,b |
  for i in 0..(AllDevicesController.all_devices.length - 1) do
    a[i][4] <=> b[i][4]
  end
end

I've also tried:

AllDevicesController.all_devices.sort do | a,b |
  a[][4] <=> b[][4]
end

Both methods have not worked.

I was using this as a reference: http://ariejan.net/2007/01/28/ruby-sort-an-array-of-objects-by-an-attribute/

I imagine I'm missing something rubyish that makes this really easy.

You can't use <=> with nil .

Your code should be something like this:

AllDevicesController.all_devices.sort do |a, b|
  a[4].nil? ? -1 : b[4].nil? ? 1 : a[4] <=> b[4]
end

This will put the sub-arrays that have no element of index 4 at the beginning of the result. To do it the other way around, swap -1 with 1 .

You could also use sort_by instead of sort . I think this has been introduced in Ruby 1.8.7 (so it might not work if you are using an older version). It goes something like:

AllDevicesController.all_devices.sort_by { |e| e.nil? ? 0 : e[4] }

This will treat sub-arrays with no 4th element as if it was 0. Change this constant to suit you.

EDIT:

After you adjusted the input, it is now clear you were very close to the right answer. Your code should have been:

AllDevicesController.all_devices.sort do |a, b|
  a[4] <=> b[4]
end

Or simple (assuming Ruby 1.8.7 or more):

AllDevicesController.all_devices.sort_by { |e| e[4] }

In both cases, the variables a and b will contain elements of the original array, this is why you can directly access an element in any position (and you don't need something like a[][4] , which is incorrect Ruby syntax).

sort_by

Instead of using spaceship operator ( <=> ) give a try to sort_by

device_array.sort_by { |el| el[4] }

Though if you know that the forth element is the last one, you can use el.last too in the block.

Ruby docs: Enumerable#sort_by

The 4th element is actually at index 3, which means you would do it like this:

all_devices.sort do |a, b|
  a[3] <=> b[3]
end

If you really want to sort the elements at index 4 (which doesn't exist for the first element of all_devices ), then you need to add comparison to the NilClass first:

class NilClass
  def <=> (other)
    1
  end
end

all_devices.sort do |a, b|
  a[4] <=> b[4]
end

This will sort nil to the end. Change the return value of <=> to -1 to sort them to the front.

I know the question has been answered, and I'm not going to address it now, but..

Are you sure an array would be the best fit for that data? I'm talking about these data elements: ["name1", "type1", ["A", "N", "N"], ["Attribute", "device_attribute"], 9]

Seems like a Struct or something might be more appropriate and manageable for this, and then you can have an array of Structs. Just an idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM