Ruby разбивает целое число на массив целых чисел

#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