tardjo
tardjo

Reputation: 1654

how to sort array in ruby if i have nil object inside array?

how to sort array if i have array like this in ruby?

example :

my_array = ["12 months", "13 months", nil, nil, "12"]

i want get result like this :

 my_array = ["12", "12 months", "13 months", nil, nil]

when i try :

my_array.sort{|x, y| x <=> y}

i get error like this :

ArgumentError Exception: comparison of String with nil failed

how to fix it?

thanks before

Upvotes: 2

Views: 812

Answers (4)

Joshua Pinter
Joshua Pinter

Reputation: 47481

Create a Array#sort_with_nils method.

If you need this more than once or twice, it could be useful to add an initializer and add a #sort_with_nils method to Array. Something like this:

config/initializers/array.rb

def sort_with_nils( nils_first: true )
  nils = [ nil ] * ( self.tally[ nil ] || 0 ) # Counts the number of `nil` values and create an Array of that number of `nil` values. We will add this to the beginning or the end of the Array after sorting.

  if nils_first
    nils + ( self - [ nil ] ).sort
  else
    ( self - [ nil ] ).sort + nils
  end
end

An example:

myarray = [ 1, 2, nil, 4, nil ]
#=> [1, 2, nil, 4, nil]
myarray.sort_with_nils
#=> [nil, nil, 1, 2, 4]
myarray
#=> [1, 2, nil, 4, nil]
myarray.sort_with_nils( nils_first: false )
#=> [1, 2, 4, nil, nil]
myarray
#=> [1, 2, nil, 4, nil]

What I like about this is that:

  1. It doesn't matter if your Array contains Strings or Integers or anything else so you don't need to use arbitrary values to assign to nil to sort them last or first.

  2. It uses the standard .sort method so nothing is overwritten and leverages the power already there.

  3. You can sort nil values first (default) or last (by passing nils_first: false param).

  4. It retains the number of nil values, if that's important, and then you can use .uniq to reduce it to a single nil value if you like.

  5. The operation is not in-place and so it doesn't affect the original Array object.

Upvotes: 0

Sumak
Sumak

Reputation: 1051

You can compare a 2 dimensions array: whether string is nil & the string itself.

my_array = ["12 months", "13 months", nil, nil, "12"]

my_array.sort_by { |x| [x ? 0 : 1, x] }
=> ["12", "12 months", "13 months", nil, nil]

Upvotes: 0

Michael Kohl
Michael Kohl

Reputation: 66837

Here's a nice short way to do it, but it will only work if you can come up with a string that's guaranteed to be "greater than" all the strings in the array (which was easy here):

my_array.sort_by { |x| x || "Z" }
=> ["12", "12 months", "13 months", nil, nil]

Upvotes: 2

Nick Veys
Nick Veys

Reputation: 23939

Handle the nil, this will get you close...

2.1.2 :010 > my_array.sort { |x,y| x && y ? (x <=> y) : (x ? -1 : 1) }
 => ["12", "12 months", "13 months", nil, nil] 

It's not exact, '12' comes before the rest based on the <=> comparison. If you want more control you'll need to have a more complex comparison block.

Upvotes: 3

Related Questions