MrPizzaFace
MrPizzaFace

Reputation: 8086

How can I split a string into chunks?

I have a VERY long string of numbers (1000 characters). I would like to break it down into chucks of 5 and insert into an array arr.

str = "7316717653133062491922511967442657474206326239578318016 ..."

I tried each_slice but when I attempt to require 'enumerator' #=> irb says: false

str.each_slice(5).to_a

I would like the output to look like:

arr = [ "73167", "17653", "33062", ... ] 

How can this be attained?

Upvotes: 2

Views: 6783

Answers (5)

akim
akim

Reputation: 8769

I personally followed the idea of user8556428, to avoid the costly intermediate values that most proposals introduce, and to avoid modifying the input string. And I want to be able to use it as a generator (for instance to use s.each_slice.with_index).

My use case is really about bytes, not characters. In the case of character-size, strscan is a fine solution.

class String
    # Slices of fixed byte-length.  May cut multi-byte characters.
    def each_slice(n = 1000, &block)
        return if self.empty?

        if block_given?
            last = (self.length - 1) / n
            (0 .. last).each do |i|
                yield self.slice(i * n, n)
            end
        else
            enum_for(__method__, n)
        end
    end
end

Upvotes: 1

user8556428
user8556428

Reputation: 11

I would be careful using .chars because it has to allocate a separate array with the string's characters. In general I recommend using blocks if available or indexing since it will run faster and be more efficient memory-wise. In the past I've used a splitter with blocks like:

def splitter(input, chunk_size = 2, &block)
  (0..input.length/chunk_size - 1).each do |i|
    yield input.slice(i * chunk_size, chunk_size) if block_given?
  end
end

:008 > splitter("test\nwow") {|x| p x}
"te"
"st"
"\nw"
"ow"
 => 0..3

Upvotes: 1

patm
patm

Reputation: 1486

I would go using regexp. I think - without doing any testing - that it's a way faster solution:

Here's some code:

2.0.0-p247 :001 > string = '1231249081029381028401982301984870895710394871023857012378401928374102394871092384710398275018923501892347'
 => "1231249081029381028401982301984870895710394871023857012378401928374102394871092384710398275018923501892347" 
2.0.0-p247 :002 > string.scan(/\d{4}/)
 => ["1231", "2490", "8102", "9381", "0284", "0198", "2301", "9848", "7089", "5710", "3948", "7102", "3857", "0123", "7840", "1928", "3741", "0239", "4871", "0923", "8471", "0398", "2750", "1892", "3501", "8923"] 
2.0.0-p247 :003 > 

NOTE: I'm using 4 chars in my example not 5.. But you get the idea.

Upvotes: 2

Dylan Markow
Dylan Markow

Reputation: 124449

The problem is that you're trying to perform an enumerable method on a non-enumerable object (a string). You can try using scan on the string to find groups of 5:

arr = str.scan /.{1,5}/

If you wanted to go the enumerable route, you could first break up the string into a character array, get groups of 5, then join them back into 5-character strings:

arr = str.chars.each_slice(5).map(&:join)

Upvotes: 13

Senjai
Senjai

Reputation: 1816

Don't know why you're requiring enumerable, it's in ruby core and doesn't need to be required.

arr = []
until string.empty?
  arr << string.slice!(0..4)
end

Upvotes: 5

Related Questions