Сортировать массив строк с цифрами и символами в ruby - PullRequest
0 голосов
/ 28 ноября 2018

У меня есть массив с заданными строками

array = [
  "1mo-30-super",
  "1mo-40-classic",
  "1mo-30-classic",
  "1mo-110-super",
  "1mo-20-extra",
  "6mo-21-super",
  "6mo-11-super",
  "12mo-21-classic",
  "12mo-21-super"
]

Как мне отсортировать массив так, чтобы он шел в числовом порядке, затем в алфавитном порядке, чтобы массив отображался так:

array = [
  "1mo-20-extra",
  "1mo-30-classic",
  "1mo-30-super",
  "1mo-40-classic",
  "1mo-110-super",
  "6mo-11-super",
  "6mo-21-super",
  "12mo-21-classic",
  "12mo-21-super"
]

Ответы [ 3 ]

0 голосов
/ 28 ноября 2018

Вы ищете "естественную" сортировку, в которой числовые подстроки будут сравниваться как числа, а нечисловые части будут сравниваться как строки.Достаточно удобно, что массивы в Ruby сравнивают поэлементно, и ваш формат довольно регулярен, так что вы можете обойтись без вызова #sort_by и небольшого искажения для преобразования "12mo-21-classic" в [12, 'mo-', 21, '-classic'].Примерно так, например:

# This is a bit complicated so we'll give the logic a name.
natural_parts = ->(s) { s.match(/(\d+)(\D+)(\d+)(\D+)/).to_a.drop(1).map.with_index { |e, i| i.even?? e.to_i : e } }
array.sort_by(&natural_parts)
0 голосов
/ 28 ноября 2018
array.sort_by { |s| [s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')] }
  #=> ["1mo-20-extra", "1mo-30-classic", "1mo-30-super", "1mo-40-classic", "1mo-110-super",
  #    "6mo-11-super", "6mo-21-super", "12mo-21-classic", "12mo-21-super"]

При сортировке массивов метод Arrays # <=> используется для упорядочивания пар массивов.См. Третий абзац документа для объяснения того, как это делается.

Массивы, используемые для сортировки, выглядят следующим образом.

array.each do |s|
  puts "%-15s -> [%2d, %3d, %s]" % [s, s.to_i, s[/(?<=-)\d+/].to_i, s.gsub(/\A.+-/,'')]
end

1mo-30-super    -> [ 1,  30, super]
1mo-40-classic  -> [ 1,  40, classic]
1mo-30-classic  -> [ 1,  30, classic]
1mo-110-super   -> [ 1, 110, super]
1mo-20-extra    -> [ 1,  20, extra]
6mo-21-super    -> [ 6,  21, super]
6mo-11-super    -> [ 6,  11, super]
12mo-21-classic -> [12,  21, classic]
12mo-21-super   -> [12,  21, super]

(?<=-) - это положительный взгляд за спиной .Требуется, чтобы совпадению предшествовал дефис./\A.+-/ соответствует началу строки, за которым следует один или несколько символов, за которыми следует дефис.Поскольку регулярные выражения по умолчанию жадные , совпадение заканчивается на втором дефисе.

Обратите внимание, что использование регулярных выражений необязательно:

array.sort_by { |s| [s.to_i, s[s.index('-')+1..-1].to_i, s[s.rindex('-')+1..-1]] }
0 голосов
/ 28 ноября 2018

Вы можете объединить несколько вызовов #sort методов, каждый из которых сортируется по отдельной части строки (начиная с той, которая имеет наименьший приоритет):

array.sort { |a,b| a.match(/-(.*)$/)[1] <=> b.match(/-(.*)-/)[1] } # sort by last element ('classic', 'super')
     .sort { |a,b| a.match(/-(\d+)-/)[1].to_i <=> b.match(/-(\d+)-/)[1].to_i } # sort by the number between dashes 
     .sort { |a,b| a.to_i <=> b.to_i  } # sort by the initial number

=> ["1mo-20-extra",
 "1mo-30-classic",
 "1mo-30-super",
 "1mo-40-classic",
 "1mo-110-super",
 "6mo-11-super",
 "6mo-21-super",
 "12mo-21-super",
 "12mo-21-classic"]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...