27

Let's say I have an unsorted array from 1 to 10, as shown below...

a = ["3", "5", "8", "4", "1", "2", "9", "10", "7", "6"]

If I use the sort method on this array, it returns this...

a.sort = ["1", "10", "2", "3", "4", "5", "6", "7", "8", "9"]

As you can see, the 10, appears before the 2, which is incorrect. How can I sort these numbers so that 10 appears correctly?

EDIT: Thank you all for your responses. I should explain my problem a little better. The array I need sorted is for an e-commerce price list. So the array appears as follows...

a = ["0-10", "11-20", "21-30", "31-40" etc.]

So the strings cannot be converted to integers. I should have put this when I wrote the question. I did not think that there would be much difference in the fix. My mistake, I apologise for making this assumption! How though can I sort this array? Thanks!

5
  • I think this question has already been answered: stackoverflow.com/questions/1955646/… Commented Oct 11, 2011 at 13:42
  • Just posted an answer to your updated question Commented Oct 11, 2011 at 15:21
  • I suggest you post a new question with updated description, since all the answers are base on the wrong description. Commented Oct 11, 2011 at 15:34
  • Since #to_i will interpret leading numeric characters in a string, the methods using #to_i will work for your example above. Commented Oct 11, 2011 at 17:02
  • I dont think it will. '10-11'.to_i is 10. try it Commented Apr 1, 2015 at 21:02

8 Answers 8

55

I'll throw another method out there since it's the shortest way I can think of

a.sort_by(&:to_i)
Sign up to request clarification or add additional context in comments.

3 Comments

This is equivalent to bricker's answer, so his comment about the number of times to_i is called compared to Matt's solution applies here as well.
What is &:? That's a new syntax to me.
@nipponese That is a good question. Here is a good thread which explains it in detail stackoverflow.com/questions/1961030/….
10

As your updated question states:

array.sort_by {|elt| ary = elt.split("-").map(&:to_i); ary[0] + ary[1]}

even geekier:

array.sort_by {|elt| ary = elt.split("-").map(&:to_i).inject(&:+)}

Comments

7

If you convert all strings to integers beforehand, it should work as expected:

a.map(&:to_i).sort
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

2 Comments

Doesn't really matter for such a small array, but your method would be faster than mine for large arrays, calling to_i only half as many times. +1
@bricker Matt's method also uses more memory as it creates a new array with map that your method doesn't. (You could use the in-place map map!, but that might not always be a viable option in your code)
6
a.sort { |a,b| a.to_i <=> b.to_i }

4 Comments

use Enumerable#sort_by instead
Why? The sort method works perfectly, I'd like to know why you recommend sort_by.
By definition xs.sort { |a, b| a.method <=> b.method } is totally equivalent to xs.sort_by(&:method). Why use sort when you have a shorter built-in exactly designed for this task? If that's not enough, there are also performance reasons, c&p from the docs: "As of Ruby 1.8, the method Enumerable#sort_by implements a built-in Schwartzian Transform, useful when key computation or comparison is expensive."
@tokland Thanks for the point about Schwartzian transforms occurring behind the scenes.
2

Another option, that can be used in your example or others arrays like

a = ["teste", "test", "teste2", "tes3te", "10teste"]

is:

a.sort_by! {|s| s[/\d+/].to_i}

Comments

1

The reason for this behavior is that you have an array of strings and the sort that is being applied is string-based. To get the proper, numeric, sorting you have to convert the strings to numbers or just keep them as numbers in the first place. Is there a reason that your array is being populate with strings like this:

a = ["3", "5", "8", "4", "1", "2", "9", "10", "7", "6"]

Rather than numbers like this:

a = [3, 5, 8, 4, 1, 2, 9, 1, 7, 6]

?

Comments

0

The cheap way would be to zero fill on the left and make all numbers 2 digit.

1 Comment

Cheap & best way is to sort them as integers instead of strings.
0

For the special case (e-commerce price list) you mentioned

a = ["0-10", "11-20", "21-30", "31-40"]

let's add a few more values to this array (as it was mentioned as price list). so

a = ["0-10", "11-20", "120-150", "110-120", "21-30", "31-40"]

we can sort such an array using the following

a.sort.sort_by(&:length)

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.