Skip to content

Instantly share code, notes, and snippets.

@tristil
Created October 28, 2013 00:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tristil/7189713 to your computer and use it in GitHub Desktop.
Save tristil/7189713 to your computer and use it in GitHub Desktop.
# Based on Dave Koelle's Alphanum algorithm
# Rik Hemsley, 2007
# WegoWise Inc., 2013
# See also http://rikkus.info/arch/sensible_sort.rb
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
module Enumerable
def sensible_sort
sort { |a, b| grouped_compare(a, b) }
end
def sensible_sort!
sort! { |a, b| grouped_compare(a, b) }
end
def sensible_sort_by(&block)
sort { |a, b| grouped_compare(block.call(a), block.call(b)) }
end
def sensible_sort_by!(&block)
sort! { |a, b| grouped_compare(block.call(a), block.call(b)) }
end
private
def grouped_compare(a, b)
loop {
a_chunk, a = extract_alpha_or_number_group(a)
b_chunk, b = extract_alpha_or_number_group(b)
# Don't try to compare numeric to string if one of group is not numeric
unless [a_chunk, b_chunk].all? {|chunk| chunk.is_a?(Numeric) }
a_chunk, b_chunk = [a_chunk, b_chunk].map(&:to_s)
end
ret = a_chunk <=> b_chunk
return -1 if a_chunk == ''
return ret if ret != 0
}
end
def extract_alpha_or_number_group(item)
matchdata = /([A-Za-z]+|[\d]+)/.match(item)
if matchdata.nil?
["", ""]
else
chunk = matchdata[0]
chunk = chunk.to_i if chunk =~ /\A\d+\Z/
[chunk, item = item[matchdata.offset(0)[1] .. -1]]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment