Ruby有一个natural_sort_by方法吗?

时间:2010-11-02 14:58:35

标签: ruby-on-rails ruby

我有一堆带有一堆属性的文件。其中一个属性是文件名,这就是我要对列表进行排序的方式。但是,列表如下:filename 1,filename 2,filename 10,filename 20。

ruby​​ sort_by方法产生了这个:

files = files.sort_by { |file| file.name }
=> [filename 1, filename 10, filename 2, filename 20]

我想要一个更易读的列表,如文件名1,文件名2,文件名10,文件名20

我找到了natural_sort gem,但它似乎只能像sort方法一样工作。我需要一些东西,我可以指定对数组进行排序的内容。

任何帮助?

7 个答案:

答案 0 :(得分:24)

这是对“自然”排序方法的另一种看法:

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

"Filename 10"之类的内容转换为一个简单数组,其中浮点数代替数字[ "Filename", 10.0 ]

您可以在列表中使用此功能:

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

这具有在不可预测的位置处理任意数字的优点。该块中的偏执.to_s调用是为了确保在排序时存在字符串而不是无意的nil

答案 1 :(得分:13)

字符串自然排序的通用答案

array.sort_by {|e| e.split(/(\d+)/).map {|a| a =~ /\d+/ ? a.to_i : a }}

答案 2 :(得分:7)

我创建了一个natural sort gem。它可以按如下属性排序:

# 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">]

答案 3 :(得分:6)

只要文件总是以"file #"命名,就可以

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

这会拆分空间,并抓取数字进行排序。

答案 4 :(得分:0)

natural_sort gem

安装

gem "natural_sort"

用法

list = ["a10", "a", "a20", "a1b", "a1a", "a2", "a0", "a1"]
list.sort(&NaturalSort) # => ["a", "a0", "a1", "a1a", "a1b", "a2", "a10", "a20"]

答案 5 :(得分:0)

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 }}

即使匹配项的类型不同,也可以在sort_by方法中按数组比较数组。即使有更深层的嵌套数组。示例:

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

这里的要点是,在排序过程中,如果某项是嵌套数组,则我们会事先将其转换为字符串。而且,如果字符串的一部分仅包含数字,则我们不会将其转换为数值,而是使用空格扩展它们,例如:

30 #=> "                         30"

这样,所有对象都是兼容的字符串,并且如果位置上匹配的对象仅是数字,则排序将能够比较它们,从而得出数字排序。

答案 6 :(得分:-3)

正确排序。这里的问题是名称不好以您想要的方式排序。在字符串中,10在2之前,21在5之前。

如果你想让它像数字一样排序,你有两种方法:

1 - 更改所有列表,在只有一位数的数字之前添加前导0。

2 - 按照威廉的建议,将名称变为aplit,将字符串转换为整数并按其排序。

我建议选项1,因为第二个依赖于名称的padronization。