Reputation: 2430
I have an array in Ruby which has values as follows
xs = %w(2.0.0.1
2.0.0.6
2.0.1.10
2.0.1.5
2.0.0.8)
and so on. I want to sort the array such that the final result should be something like this :
ys = %w(2.0.0.1
2.0.0.6
2.0.0.8
2.0.1.5
2.0.1.10)
I have tried using the array.sort
function, but it places "2.0.1.10"
before "2.0.1.5"
. I am not sure why that happens
Upvotes: 2
Views: 848
Reputation: 67860
Using a Schwartzian transform (Enumerable#sort_by), and taking advantage of the lexicographical order defined by an array of integers (Array#<=>):
sorted_ips = ips.sort_by { |ip| ip.split(".").map(&:to_i) }
Can you please explain a bit more elaborately
You cannot compare strings containing numbers: "2" > "1"
, yes, but "11" < "2"
because strings are compared lexicographically, like words in a dictionary. Therefore, you must convert the ip into something than can be compared (array of integers): ip.split(".").map(&:to_i)
. For example "1.2.10.3"
is converted to [1, 2, 10, 3]
. Let's call this transformation f
.
You could now use Enumerable#sort
: ips.sort { |ip1, ip2| f(ip1) <=> f(ip2) }
, but check always if the higher abstraction Enumerable#sort_by
can be used instead. In this case: ips.sort_by { |ip| f(ip) }
. You can read it as "take the ips
and sort them by the order defined by the f
mapping".
Upvotes: 18
Reputation: 2883
Split your data into chunks by splitting on '.'
. There is no standard function to do it as such so you need to write a custom sort to perform this.
And the behaviour you said about 2.0.1.10
before 2.0.1.5
is expected because it is taking the data as strings and doing ASCII comparisons, leading to the result that you see.
arr1 = "2.0.0.1".split('.')
arr2 = "2.0.0.6".split('.')
Compare both arr1
and arr2
element by element, for all the data in your input.
Upvotes: 1