Sort strings and numbers in Ruby

后端 未结 6 1950
一生所求
一生所求 2020-12-08 12:48

I want to sort an array by strings first and then numbers. How do I do this?

相关标签:
6条回答
  • 2020-12-08 12:58

    A general trick for solving tricky sorts is to use #sort_by, with the block returning an array having the primary and secondary sort order (and, if you need it, tertiary, etc.)

    a = ['foo', 'bar', '1', '2', '10']  
    b = a.sort_by do |s|
      if s =~ /^\d+$/
        [2, $&.to_i]
      else
        [1, s]
      end
    end
    p b    # => ["bar", "foo", "1", "2", "10"]
    

    This works because of the way array comparison is defined by Ruby. The comparison is defined by the Array#<=> method:

    Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.

    0 讨论(0)
  • 2020-12-08 12:59

    Sort an array of mixed numbers and strings by putting the numbers first, and in order, followed by the strings second, and in order.

    >> a = [1, 2, "b", "a"]
    
    >> a.partition{|x| x.is_a? String}.map(&:sort).flatten
    => ["a", "b", 1, 2]
    
    0 讨论(0)
  • 2020-12-08 13:06

    Here is a somewhat verbose answers. Divide the array into two sub arrays: strings and numbers, sort them and concat them.

    array = [1, 'b', 'a', 'c', 'd', 2, 4, 3]
    strings = []
    numbers = []
    array.each do |element|
      if element.is_a? String
        strings << element
      else
        numbers << element
      end
    end
    sorted_array = strings.sort + numbers.sort
    sorted_array # ['a', 'b', 'c', 'd', 1, 2, 3, 4]
    
    0 讨论(0)
  • 2020-12-08 13:07
    a = ['1', '10', '100', '2', '42', 'hello', 'x1', 'x20', 'x100', '42x', '42y', '10.1.2', '10.10.2', '10.8.2']
    a.map {|i| i.gsub(/\d+/) {|s| "%08d" % s.to_i } }.zip(a).sort.map{|x,y| y}
    # => ["1", "2", "10", "10.1.2", "10.8.2", "10.10.2", "42", "42x", "42y", "100", "hello", "x1", "x20", "x100"] 
    
    0 讨论(0)
  • 2020-12-08 13:13

    Normally, alphabetization is done with numbers first. If you want to alphabetize something where letters are alphabetized before numbers, you will need to alter the compare function used.

    # I realize this function could be done with less if-then-else logic,
    # but I thought this would be clearer for teaching purposes.
    def String.mysort(other)
      length = (self.length < other.length) ? self.length : other.length
      0.upto(length-1) do |i|
        # normally we would just return the result of self[i] <=> other[i]. But
        # you need a custom sorting function.
        if self[i] == other[i]
          continue # characters the same, skip to next character.
        else
          if self[i] ~= /[0-9]/
            if other[i] ~= /[0-9]/
              return self[i] <=> other[i]  # both numeric, sort normally.
            else
              return 1  # self is numeric, other is not, so self is sorted after.
            end
          elsif other[i] ~= /[0-9]/
            return  -1  # self is not numeric, other is, so self is sorted before.
          else
            return self[i] <=> other[i]    # both non-numeric, sort normally.
          end
        end
      end
    
      # if we got this far, the segments were identical. However, they may
      # not be the same length. Short sorted before long.
      return self.length <=> other.length
    end
    
    ['0','b','1','a'].sort{|x,y| x.mysort(y) } # => ['a', 'b', '0', '1']
    
    0 讨论(0)
  • 2020-12-08 13:13

    If you are trying to sort Mixed case and numbers, only a few people on earth can do it outside of proprietary applications. It is a secret with a sucker punch. You must use a qsort which makes sorting easy until you mix cases (upper and lower case letters). Then college, books and internet leave you hanging. This hack worth its weight in gold and is the brass ring of programming for all reasons.

    To sort numbers with words you must convert numbers into string. You must presort using upper case. If you have the words "Ant", "ant" and "anT" on the less they should all point to the word "ANT" to the upper case sort list. You will then create a list (array) of just these three words ["Ant", "ant" and "anT"] and use qsort as a tie breaker to sort them.

    You then insert them into a final sorting array. It is fairly difficult by design. "A" is 65 on ascii and 'a' is 97 with lots of garbage characters between 'Z' and 'a'! It is no accident! It a conspiracy I tell you!

    You could create a sorting table the more sanely groups the characters like :

    A, a, B, b, C, c, D, d, E, e, F, f, G, g, H, h, I, i, J, j, K, k, L, l, M, m, N, n, ... 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 ...

    building the table around this block starting with " "(space) ascii 32 upto 128. you will probably want to reorder the numbers in sequence the A 65 is for example only.

    This makes it easier but will likely cause a performance hit being outside the macros of most programming languages. Good luck!

    0 讨论(0)
提交回复
热议问题