2010-11-02 6 views
14

У меня есть список файлов с кучей атрибутов. Одним из атрибутов является имя файла, в котором я хотел бы отсортировать список. Тем не менее, этот список можно продолжать что-то вроде этого: имя файл, имя файла-2, имени файла, имя файла 10 20.Есть ли метод natural_sort_by для Ruby?

Метод рубина sort_by производит это:

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

Я хотел бы более удобочитаемый список, как имя файл- , filename 2, filename 10, filename 20

Я нашел драгоценный камень natural_sort, но, похоже, работает только как способ сортировки. Мне нужно что-то, где я могу указать, для чего сортировать массив.

Любая помощь?

ответ

7

Пока файлы всегда называются "file #", вы могли бы сделать

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

Это расщепляется на пространстве, и захватывает номер, чтобы сделать сортировку.

+0

Что означает "[1]" делать? –

+0

[1] возвращает второй элемент массива, возвращаемый split, в этом случае - число. – Teoulas

+2

В качестве альтернативы вы можете использовать '.last' вместо' [1] ', поэтому' files.sort_by {| f | f.name.split ("") .last.to_i} ' – William

22

Вот еще один взгляд на «естественный» метод сортировки:

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 в этом блоке должен гарантировать, что при сортировке есть строка, а не непреднамеренное .

+0

Это приятно. Благодаря! –

+0

Wow thats magic. В моем случае использования идентификаторы могут быть разделены символом «.». Таким образом, я удаляю два «\». в регулярном выражении, используемом в scan(). Я не думаю, что это может сломать что угодно. –

+0

Это будет означать, что любые значения с десятичной точкой будут интерпретироваться как отдельные числа. 10.2 придет после 10.1, но до 10.11. – tadman

6

Я создал 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

Он сортируется правильно. Проблема здесь в том, что имена не очень хороши для сортировки так, как вы хотите. В помощи строки, 10 предшествуют 2 и 21 предшествует 5.

Если вы хотите, чтобы отсортировать его, как это было число, у вас есть 2 подхода:

1 - изменить все свои списки, чтобы добавить ведущий 0 перед цифрами с одной цифрой.

2 - Сделайте, как предложил Уильям, aplit имя, преобразуйте строку в целое и сортируйте по ней.

Я бы рекомендовал вариант 1, так как второй полагается на прокрутку имен.

12

общий ответ для строк естественного вида

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

Это не удается с помощью простых массивов, таких как '[" a1 "," aa "]' потому что '[" a ", 1] <=> [" a "," a "]' возвращает nil и 'sort_by 'не нравится. (Я не уверен, почему это возвращает нуль.) –

+0

Предполагается, что \ d +, мой плохой – shurikk

Смежные вопросы