Sha256: d5fab5478675c60c83eb450b52ce6c8999e5721532a5ed9b06522a1f31243f08

Contents?: true

Size: 1.53 KB

Versions: 1

Compression:

Stored size: 1.53 KB

Contents

module Enumerable
  def sort_by_alphanum
    sort do |a, b|
      if block_given?
        grouped_compare(yield(a), yield(b))
      else
        grouped_compare(a, b)
      end
    end
  end

  def sort_by_alphanum!
    sort! do |a, b|
      if block_given?
        grouped_compare(yield(a), yield(b))
      else
        grouped_compare(a, b)
      end
    end
  end

  private

  ALL_NUM = /\d+/
  ALL_ALPHA = /[A-Za-z]+/
  NON_ALPHANUM = /[^A-Za-z0-9]+/

  def grouped_compare(a, b)
    a_scanner = StringScanner.new(a)
    b_scanner = StringScanner.new(b)
    # each loop has to do exactly 1 non-nil-scan on both scanners or return a non-zero value.
    loop do
      ret = \
        if a_scanner.eos?
          -1
        elsif (a_chunk = a_scanner.scan(ALL_NUM))
          if (b_chunk = b_scanner.scan(ALL_NUM))
            if a_chunk.to_i != b_chunk.to_i
              a_chunk.to_i <=> b_chunk.to_i
            else # 03 vs 3
              a_chunk <=> b_chunk
            end
          elsif b_scanner.scan(ALL_ALPHA)
            -1
          else # NON_ALPHANUM
            1
          end
        elsif (a_chunk = a_scanner.scan(ALL_ALPHA))
          if (b_chunk = b_scanner.scan(ALL_ALPHA))
            a_chunk <=> b_chunk
          else # ALL_NUM or NON_ALPHANUM
            1
          end
        else # NON_ALPHANUM
          a_chunk = a_scanner.scan(NON_ALPHANUM)
          if (b_chunk = b_scanner.scan(NON_ALPHANUM))
            a_chunk <=> b_chunk
          else
            -1
          end
        end
      return ret if ret != 0
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
roqua-support-0.4.2 lib/roqua/core_ext/enumerable/sort_by_alphanum.rb