Journal benhocking's Journal: Sorting intelligently 2
I've had this problem in an archeology database I've been working on. Simply, I want "Room 11" to come after "Room 2", "House 3" to come before "House 11", etc. Now, there are some simple ways to handle this problem, if those are the only types of entries. However, there's also "Room A", "Room 11A", and possibly even "Room 11A-2" and "Room 11A-11" (again, 2 < 11). So, how to solve this problem? Well, since this is a Ruby on Rails project, I've solved it like this:
def smartComp (x,y)
digitRegex =
if xMatch = digitRegex.match(x)
if yMatch = digitRegex.match(y)
xn = xMatch[0].to_i
yn = yMatch[0].to_i
if xn == yn
# Recursive call on remainder
smartComp(xMatch.post_match, yMatch.post_match)
else
xn <=> yn
end
else
1 # numbers are "greater than" letters
end
elsif x.size > 0
if digitRegex =~ y
-1 # letters are "less than" numbers
elsif y.size > 0
charRegex =
xMatch = charRegex.match(x)
yMatch = charRegex.match(y)
if xMatch[0] == yMatch[0]
# Recursive call on remainder
smartComp(xMatch.post_match, yMatch.post_match)
else
x.upcase <=> y.upcase # Case insensitive
end
else
x <=> y
end
else
x <=> y
end
end
Thoughts, comments? Feel free to be brutal.
For those who don't know how sorting works in Ruby, you could sort using this routine by:
array.sort{ |x,y| smartComp(x,y) }
In case you're wondering why I didn't actually create a smartSort routine that surrounds this, it has to do with the fact that the actual sort I'm doing is somewhat more complex.
My main concern is that it feels like what I'm doing is intuitively simple, the "Ruby way" would seem to dictate a simple solution, and my solution feels somewhat inelegant to me.
split, avoid recursion (Score:1)
Disclaimer: My ruby is weak -- I mostly think in perl and then try to translate. But I agree that your method is inelegant (and the recursion is likely to be costly, especially since this is likely to be called n ln n times in a sort.)
I think you should string.split(/[\d]+/) your inputs (x,y) into arrays (ax, ay), then iterate over the arrays, selecting a numeric or string compare on each pair of elements (ax[i], ay[i]) until one or the other "wins" or you run out of elements.
A more c-like method wou
Worth looking at (Score:2)