#ruby-on-rails #ruby
Вопрос:
Допустим, у меня есть большое целое 293649214912
число, я хочу получить следующий результат:
[2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
Комментарии:
1. Рассмотрим результаты
293649214912 % 10000
и293649214912 / 10
.2. @Chris : Как насчет преобразования числа в строку, разбиения строки на массив из 4-значных частей, а затем преобразования этого числа обратно в массив целых чисел?
Ответ №1:
Это можно сделать довольно легко, используя Integer#digits
и некоторые основные методы итерации из Enumerable
:
def split_integer_into_sliding_groups_of_4_digits(n)
n.
digits.
each_cons(4).
map do |arr|
arr.each_with_index.reduce(0) {|acc, (d, i)| acc d * 10 ** i }
end
end
В качестве альтернативы это также можно сделать с помощью манипуляции со строками:
def split_integer_into_sliding_groups_of_4_digits(n)
n.to_s.each_char.each_cons(4).map(amp;:join).map(amp;:to_i)
end
Или смесь того и другого:
def split_integer_into_sliding_groups_of_4_digits(n)
n.digits.reverse.each_cons(4).map(amp;:join).map(amp;:to_i)
end
Ответ №2:
Можно преобразовать целое число в строку и использовать регулярное выражение со строкой#gsub или строкой#scan.
str = 293649214912.to_s
#=> "293649214912"
r = /(?=(d{4}))/
str.gsub(r).map { $1.to_i }
#=> [2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
Это использует форму String#gsub
, которая принимает один аргумент (шаблон) и не блокирует, возвращая перечислитель 1, который я привязал к перечисляемой#карте.
Регулярное выражение соответствует строке нулевой длины (подумайте о расположении между последовательными цифрами) при условии, что за этим местоположением следуют четыре цифры. (?=(d{4}))
это положительный индикатор, который утверждает, что за совпадением сразу следуют четыре цифры. Эти четыре цифры не являются частью возвращаемого совпадения. Четыре цифры сохраняются в группе захвата 1, поэтому их можно извлечь внутри блока. Переменная блока _
содержит пустую строку (совпадение). Я использовал подчеркивание, чтобы указать, что оно не используется при расчете блока (общее соглашение).
В качестве альтернативы, scan
может использоваться с тем же регулярным выражением.2
str.scan(r).map { |(s)| s.to_i }
#=> [2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
Вспомните, как scan
обрабатываются группы захвата: «Если шаблон содержит группы, каждый отдельный результат сам по себе является массивом, содержащим по одной записи на группу».
Обратите внимание, что:
str.scan(r)
#=> [["2936"], ["9364"], ["3649"], ["6492"], ["4921"],
# ["9214"], ["2149"], ["1491"], ["4912"]]
Если кто-то предпочитает придерживаться целых чисел, вот вариант ответа @Allan, когда его аргумент n
равен 4
:
def construct(n)
loop.with_object([]) do |_,arr|
arr.unshift(n % 10_000)
n /= 10
break arr if n < 1_000
end
end
construct(293649214912)
#=> [2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
При этом используется форма цикла ядра#, который не принимает блок и возвращает перечислитель. Я приковал этот перечислитель к перечислителю#с_объектом.
1 Эта форма gsub
не имеет ничего общего с заменой строк: перечислитель str.gsub(r)
просто генерирует совпадения.
2. Вместо этого это можно было бы написать str.scan(r).map { |a| a.first.to_i }
.
Комментарии:
1. Большое спасибо.
2. Большое спасибо, это именно то, что я искал
Ответ №3:
Вот что делает это с помощью строковых манипуляций:
def split_n(i, n)
s = i.to_s
return s.length < n ?
[] :
(0..s.length-n).map { |i| s.slice(i, n).to_i }
end
end
и целочисленная версия:
def split_n(i, n)
mask = 10**n
limit = 10**(n-1)
r = []
while i > limit do
r.unshift(i % mask)
i /= 10
end
return r
end