I am trying to sort an array with a mixture of ints and strings. Take for example:
a = ["a", "b", 5, "c", 4, "d", "a1", "a12", 3, 13, 2, "13a", "12a"]
I tried:
a.sort do |x, y|
if x.class == y.class
x <=> y
else
x.class.to_s <=> y.class.to_s
end
end
Which returns:
[2, 3, 4, 5, 13, "12a", "13a", "a", "a1", "a12", "b", "c", "d"]
The outcome I want is:
[2, 3, 4, 5, "12a", 13, "13a", "a", "a1", "a12", "b", "c", "d"]
A simple solution would be to use efficient sorting algorithms like Merge Sort, Quicksort, Heapsort, etc., that can solve this problem in O(n. log(n)) time, but those will not take advantage of the fact that there are many duplicated values in the array. A better approach is to use a counting sort.
a.sort_by { |x| [(x.to_s.match(/^\d+/) ? x.to_i : 1.0 / 0), x.to_s] }
The idea is to sort first by the numeric value and secondly by the string value. If the the string doesn't start with numeric value, forcefully consider the numeric value to be infinity.
EDIT: As OP has clarified that he wants to consider not just the leading numeric value, but all that follow, we can use the same idea, just this time we have to apply it on each individual numeric and non-numeric entity in the string:
a.sort_by do |x|
x.to_s.split(/(\D+)/).map do |y|
[(y.match(/\d/) ? y.to_i : 1.0 / 0), y]
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With