2

I have the following array of strings:

x = ["1.2", "1.1", "1.18", "1.18.3", "1.4", "1.18.20", "2.5"]

Ruby's Array sort method is sorting the strings alphabetically which results in this:

[
[0] "1.1",
[1] "1.18",
[2] "1.18.20",
[3] "1.18.3",
[4] "1.2",
[5] "1.4",
[6] "2.5",
]

I'd like to sort this array based on the numerical value within each decimal so that it returns the following order:

[
[0] "1.1",
[1] "1.2",
[2] "1.4",
[3] "1.18",
[4] "1.18.3",
[5] "1.18.20",
[6] "2.5"
]

I also can't guarantee the number of decimal points within each "ID". Any help would be greatly appreciated. Thanks!

3
  • 1
    Within each value you can use split on "." to convert them to arrays, and map to convert to integers, and an array of arrays of integers will sort in the way you require. Commented Sep 23, 2015 at 17:19
  • Yes, it's a duplicate. I see @SergioTulentsev had a great answer. Commented Sep 23, 2015 at 17:58
  • The only thing different about this question is that the number of decimal points isn't guaranteed. But the answer to the other question is the same and doesn't limit based on "#.#.#" format. Commented Sep 23, 2015 at 18:42

3 Answers 3

6

You can use rubygems version comparison for that:

x = ["1.2", "1.1", "1.18", "1.18.3", "1.4", "1.18.20", "2.5"]
x.sort_by { |i| Gem::Version.new(i) }
# => ["1.1", "1.2", "1.4", "1.18", "1.18.3", "1.18.20", "2.5"] 
Sign up to request clarification or add additional context in comments.

1 Comment

Nice. No require needed. Your answer illustrates a weakness of closing questions that are identified as duplicates: it can prevent new, novel solutions from being seen. Sure, you could post your solution as an answer to the original question, but who's going to see it?
3
x.sort_by { |s| s.split('.').map(&:to_i) }
  #=> ["1.1", "1.2", "1.4", "1.18", "1.18.3", "1.18.20", "2.5"]

This sorts like so:

[[1,1], [1,2], [1,4], [1,18,3], [1,18,20], [2,5]].sort
  #=> [[1, 1], [1, 2], [1, 4], [1, 18, 3], [1, 18, 20], [2, 5]]

See Array#<=> for an explanation of the result of the sort.

Comments

1

You can use something like this:

x.sort_by { |n|
  numbers = n.split(/\./)
  numbers.map { |a| a.to_f }
}
# => ["1.1", "1.2", "1.4", "1.18", "1.18.3", "1.18.20", "2.5"]

2 Comments

Cary's answer, which is similar to this, is superior in three respects: 1) You can avoid assigning a local variable by method chaining 2) You don't need a regex to split; a string is sufficient 3) You don't need to convert to a float; converting to integer is sufficient. But, as of now, please do not edit your answer to reflect this since that would make your answer only a duplicate of Cary's (Using & with Symbol#to_proc is a matter of preference).
@sawa Yes, I decided not to edit it for the same reason.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.