Is there a natural_sort_by method for Ruby?

前端 未结 7 1096
说谎
说谎 2020-12-06 05:06

I have a list of files with a bunch of attributes. One of the attributes is the file name which is how I would like to sort the list. However, the list goes something like t

相关标签:
7条回答
  • 2020-12-06 05:38

    Here's another take on a "natural" sort method:

    class String
      def naturalized
        scan(/[^\d\.]+|[\d\.]+/).collect { |f| f.match(/\d+(\.\d+)?/) ? f.to_f : f }
      end
    end
    

    This converts something like "Filename 10" into a simple array with floats in place of numbers [ "Filename", 10.0 ]

    You can use this on your list:

    files.sort_by! { |file| file.name.to_s.naturalized }
    

    This has the advantage of working on arbitrary numbers in unpredictable positions. The paranoid .to_s call in that block is to ensure that there is a string and not an inadvertent nil when sorting.

    0 讨论(0)
  • 2020-12-06 05:42

    Natural Sort gem.

    Install

    gem "natural_sort"

    Usage

    list = ["a10", "a", "a20", "a1b", "a1a", "a2", "a0", "a1"]
    list.sort(&NaturalSort) # => ["a", "a0", "a1", "a1a", "a1b", "a2", "a10", "a20"]
    
    0 讨论(0)
  • 2020-12-06 05:43
    array.sort_by{|x| ( x.class == Array ? x.join(" ") : x.to_s ).split(/(\d+)/).map{|x| x.to_s.strip }.select{|x| x.to_s != "" }.map{|x| x =~ /\d+/ ? x.to_s.rjust(30) : x }}
    

    This can compare arrays by arrays in the sort_by method even if the type of the matching items differ. Even if there are deeper nested arrays. Example:

    [ "3  a   22", "b  22     1", "   b  5  ", [11, 2, [4, 5]] ] #=>
    [ "3  a   22", [11, 2, [4, 5]], "   b  5  ", "b  22     1" ]
    

    The point here is that during the sort if an item is a nested array then we convert it to a string beforehand. And if parts of the string contain digits only then we do not convert them to numeric values but instead extend them with spaces, like:

    30 #=> "                         30"
    

    This way all objects will be compatible strings and the sorting will be able to compare them resulting in a numeric sort if the matching objects at their positions are numbers only.

    0 讨论(0)
  • 2020-12-06 05:52

    As long as files are always named "file #", you could do

    files.sort_by{|f| f.name.split(" ")[1].to_i }

    This splits on the space, and grabs the number to do the sorting.

    0 讨论(0)
  • 2020-12-06 05:53

    generic answer for strings natural sort

    array.sort_by {|e| e.split(/(\d+)/).map {|a| a =~ /\d+/ ? a.to_i : a }}
    
    0 讨论(0)
  • 2020-12-06 05:54

    I've created a natural sort gem. It can sort by an attribute like this:

    # Sort an array of objects by the 'number' attribute
    Thing = Struct.new(:number, :name)
    objects = [
      Thing.new('1.1', 'color'),
      Thing.new('1.2', 'size'),
      Thing.new('1.1.1', 'opacity'),
      Thing.new('1.1.2', 'lightness'),
      Thing.new('1.10', 'hardness'),
      Thing.new('2.1', 'weight'),
      Thing.new('1.3', 'shape')
      ]
    Naturally.sort_by(objects, :number)
    
    # => [#<struct Thing number="1.1", name="color">,
          #<struct Thing number="1.1.1", name="opacity">,
          #<struct Thing number="1.1.2", name="lightness">,
          #<struct Thing number="1.2", name="size">,
          #<struct Thing number="1.3", name="shape">,
          #<struct Thing number="1.10", name="hardness">,
          #<struct Thing number="2.1", name="weight">]
    
    0 讨论(0)
提交回复
热议问题