Let's say I have big integer 293649214912
I want to get the next result:
[2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
CodePudding user response:
This can be done pretty easily using Integer#digits
and some basic iteration methods from 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
As an alternative, it can also be done via string manipulation:
def split_integer_into_sliding_groups_of_4_digits(n)
n.to_s.each_char.each_cons(4).map(&:join).map(&:to_i)
end
Or a mix of the two:
def split_integer_into_sliding_groups_of_4_digits(n)
n.digits.reverse.each_cons(4).map(&:join).map(&:to_i)
end
CodePudding user response:
One could convert the integer to a string and use a regular expression with String#gsub or String#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]
This uses the form of String#gsub
that takes one argument (the pattern) and no block, returning an enumerator1 that I've chained to Enumerable#map.
The regular expression matches a zero-length string (think of the location between successive digits) provided that location is followed by four digits. (?=(\d{4}))
is a positive lookahead that asserts that the match is followed immediately by four digits. Those four digits are not part of the match that is returned. The four digits are saved in capture group 1, so they may be retrieved within the block. The block variable _
holds an empty string (the match). I've used an underscore to signal that it is not used in the block calculation (a common convention).
Alternatively, scan
could be used with the same regular expression.2
str.scan(r).map { |(s)| s.to_i }
#=> [2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
Recall how scan
treats capture groups: "If the pattern contains groups, each individual result is itself an array containing one entry per group."
Note that:
str.scan(r)
#=> [["2936"], ["9364"], ["3649"], ["6492"], ["4921"],
# ["9214"], ["2149"], ["1491"], ["4912"]]
If one chooses to stick with integers, here is a variant of @Allan's answer when his argument n
equals 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]
This uses the form of Kernel#loop that does not take a block and returns an enumerator. I've chained that enumerator to Enumerator#with_object.
1 This form of gsub
has nothing to do with string replacement: the enumerator str.gsub(r)
merely generates matches.
2. This could instead be written str.scan(r).map { |a| a.first.to_i }
.
CodePudding user response:
Here is that does it via string manipulations:
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
and a integer version:
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