Более эффективный способ, чем gsub дважды

#ruby-on-rails #ruby #regex

#ruby-on-rails #ruby #регулярное выражение

Вопрос:

У меня есть массив строк:

 orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]
  

Я делаю это, чтобы удалить начальный «#» и конечный «.1»

 orders.each do |numbers|
  puts numbers.gsub!("#", "").gsub!(/.[0-9]/, "")
end
# returns 1174, 1176 etc..
  

Конечное значение «.1» может быть любым числом до 9 .. есть ли лучший / более быстрый способ сделать это?

Комментарии:

1. Кроме того, FWIW, вы, вероятно, захотите использовать gsub, а не gsub! для вашего примера. Методы bang обычно не работают красиво, поскольку они возвращают строку только в том случае, если были выполнены замены — это может привести к незначительным ошибкам. ruby-doc.org/core-2.1.4/String.html#method-i-gsub

2. Будут ли числа всегда иметь один и тот же формат, то есть четыре цифры, десятичную дробь и одну цифру?

3. Вы ожидаете, что числа будут округлены правильно?

Ответ №1:

Один из способов сделать это:

 orders.map { |s| s.sub('#','').to_i }
#=> [1174, 1176, 1177, 1178, 1180] 
  

добавьте to_s в блок, если вам нужны строки.


В случае, если OP хочет, чтобы числа были округлены. например #1174.8 , возвращает 1175 и т. Д. Тогда это должно сработать:

 orders = ["#1174.2", "#1176.5", "#1177.2", "#1178.7", "#1180.1"]
#=> ["#1174.2", "#1176.5", "#1177.2", "#1178.7", "#1180.1"]     

orders.map { |s| s.sub('#','').to_f.round.to_s }
#=> ["1174", "1177", "1177", "1179", "1180"]
  

Комментарии:

1. Как он не справляется с конечным периодом и числом?

Ответ №2:

Я бы использовал:

 orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]
orders.map{ |n| n[/d /] } # => ["1174", "1176", "1177", "1178", "1180"]
  

/d / вернет первую найденную группу цифр, что означает, что '#' и .n будет автоматически игнорироваться.

Вот тест:

 require 'fruity'

orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]

compare do
 ttm { orders.map{ |n| n[/d /] } }
 ursus { orders.map { |item| item.gsub(/#(d )(.d)?/, '1') } }
 dinesh { orders.join.gsub(/.[0-9]#/, "#").gsub(/.[0-9]/, "").split("#") - [""] }
 sagarpandya82 { orders.map { |s| s.sub('#','').to_i.to_s } }
 infused { orders.map { |numbers| numbers.gsub(/(^#|.d$)/, '') } }
end

# >> Running each test 1024 times. Test will take about 1 second.
# >> ttm is faster than sagarpandya82 by 60.00000000000001% ± 10.0%
# >> sagarpandya82 is faster than dinesh by 2.0x ± 0.1
# >> dinesh is faster than infused by 39.99999999999999% ± 10.0%
# >> infused is faster than ursus by 10.000000000000009% ± 10.0%
  

Комментарии:

1. Это явно лучшее регулярное выражение среди предложенных. Помимо того, что он самый простой, он читается лучше всего: как вы говорите (sic), «верните первую найденную строку цифр».

Ответ №3:

 cleaned_orders = orders.map { |numbers| numbers.gsub(/(^#|.d$)/, '') }
  

cleaned_orders теперь содержит: ['1174', '1176', '1177', '1178', '1180']

(^#|.d$) соответствует #, если он находится в начале строки или в одной точке, за которой следует одна цифра в конце строки.

Ответ №4:

 orders.map { |s| s[1..-1].to_i.to_s }
  #=> ["1174", "1176", "1177", "1178", "1180"]
  

Удалите .to_s , если вам нужен массив целых чисел, а не массив строк.

Комментарии:

1. Проблема в to_i том, что он не будет правильно округляться, например 1774.6.to_i , даст 1774 , но мы, вероятно, действительно хотим 1775 . Только что заметил это и отредактировал мой ответ. Хотя в OP (или в каких-либо других ответах) упоминается / учитывается это, конечно, это должен быть правильный подход?

2. @sagarpandya82, мы не можем сказать. Например, когда я подаю свои налоги, наши налоговики округляют некоторые суммы до ближайшего доллара. Тодд сказал, что требуется усечение («Конечное значение «.1″ может быть любым числом до 9».)

Ответ №5:

Похоже, что все числа состоят из четырех цифр. Если это всегда верно, вы можете попробовать это:

 orders = ["#1174.2", "#1176.3", "#1177.2", "#1178.1", "#1180.1"]
orders.map { |n| n[1..-3] }
#=> ["1174","1176","1177","1178","1180"] 
  

Это, очевидно, работает, только если все числа имеют одинаковый формат и длину, но это намного быстрее, чем использование регулярного выражения.

Ответ №6:

Попробуйте что-то вроде этого

 orders.map { |item| item.gsub(/#(d )(.d)?/, '1') }
 => ["1174", "1176", "1177", "1178", "1180"] 
  

Это работает, даже если некоторые элементы не имеют точки в конце.

Ответ №7:

Используйте это

 orders.join.gsub(/.[0-9]#/, "#").gsub(/.[0-9]/, "").split("#") - [""]
  

ВОЗВРАТ

 => ["1174", "1176", "1177", "1178", "1180"]
  

Разбивка

Этот метод занимает 5 шагов. Это независимо от того, сколько элементов у вас в массиве.

 2.2.5 :019 > orders.join
 => "#1174.2#1176.3#1177.2#1178.1#1180.1" 
2.2.5 :020 > orders.join.gsub(/.[0-9]#/, "#")
 => "#1174#1176#1177#1178#1180.1" 
2.2.5 :021 > orders.join.gsub(/.[0-9]#/, "#").gsub(/.[0-9]/, "")
 => "#1174#1176#1177#1178#1180" 
2.2.5 :022 > orders.join.gsub(/.[0-9]#/, "#").gsub(/.[0-9]/, "").split("#")
 => ["", "1174", "1176", "1177", "1178", "1180"] 
2.2.5 :023 > orders.join.gsub(/.[0-9]#/, "#").gsub(/.[0-9]/, "").split("#") - [""]
 => ["1174", "1176", "1177", "1178", "1180"] 
  

Комментарии:

1. Это немного сложно для задачи.

2. Да, это сложнее, чем моя исходная строка методов

3. @Toddt Да, это сложно, но это попытка получить более быстрый результат

4. @Toddt я не понимаю, почему люди голосуют против этого, когда это дает ожидаемый результат. И OP попросил более быстрый способ, так что это попытка получить более быстрый результат.