1

I have an array of hashes that I would like to sort based on the :reference values. But some of the elements within the array do not have a :reference key and therefore cannot be sorted. Is there a way to ignore this field and just sort the hash elements that contain this key?

I've tried the following approach but I'm getting an argument error

ArgumentError: comparison of NilClass with String failed
  sort_by at org/jruby/RubyEnumerable.java:503



arr1 = [{:reference=> "F123",
            :name=> "test4"
          },
          {
            :reference=> "ZA4",
            :name=> "test3"
          },
          {
            :reference=> "A43",
            :name=> "test2"
          },
          {
            :name=> "test1"
          },
          {
            :name=> "homework1"
          }]

arr1 = arr1.sort_by { |hash| hash[:reference] }

puts arr1

The correct output should look like this :

=> arr1= [
        {:reference=>"A43", :name=>"test2"},
        {:reference=>"F123", :name=>"test4"},
        {:reference=>"ZA4", :name=>"test3"},
        {:name=> "test1"},
        {:name=> "homework1"}
      ]

2 Answers 2

3

You can only sort on values that can be compared, so if you've got values of different types it's best to convert them to the same type first. A simple work-around is to convert to string:

arr1.sort_by { |hash| hash[:reference].to_s }

You can also assign a default:

arr1.sort_by { |hash| hash[:reference] || '' }

Edit: If you want the nil values sorted last:

arr1.sort_by { |hash| [ hash[:reference] ? 0 : 1, hash[:reference].to_s ] }
Sign up to request clarification or add additional context in comments.

5 Comments

thank you. Is there a way to sort the array so that the hashes without the :reference keys end up at the back of the array?
I've added a method to push those to the end by adding a second element to their sorting to force them back. An array is a convenient way of doing this since it's sorted based on first element then second element.
You don't need .to_s in your last line. Strings will never be compared to nil and nil <=> nil #=> 0.
@CarySwoveland That's a bit of paranoia that some crazy stuff like false might show up in there, but you're basically right that it's extraneous.
If false an exception is raised in any event, which is probably what you want. Note that if false the first element of the sort_by array will be 1, so you will comparing false.to_s #=> "false" with nil, raising an exception. If you drop to_s, false will be compared with nil, again raising an exception (which is good). So not only does to_s not help, it's a distraction.
1

If you don't mind temporary variables, you could split the array into two arrays, one containing hashes with :reference and the other those without:

with_ref, without_ref = arr1.partition { |h| h[:reference] }

Then sort the first one:

with_ref.sort_by! { |h| h[:reference] }

And finally concatenate both arrays:

arr1 = with_ref + without_ref

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.