0

Let's say I have big integer 293649214912 I want to get the next result:

[2936, 9364, 3649, 6492, 4921, 9214, 2149, 1491, 4912]
2
  • 1
    Consider the results of 293649214912 % 10000 and 293649214912 / 10. Commented Sep 19, 2021 at 1:48
  • @Chris : How about converting the number to a string, split the string into an array 4-digit-pieces, and then convert this back to an array of integers? Commented Sep 20, 2021 at 11:14

3 Answers 3

5

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
Sign up to request clarification or add additional context in comments.

Comments

3

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 }.

1 Comment

Thanks a lot, that's exactly what i was looking for
2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.