#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 попросил более быстрый способ, так что это попытка получить более быстрый результат.