What is the best way to slice a string into chunks of a certain length in Ruby?

I was looking for an elegant and efficient way to split a string into substrings of a given length in Ruby.

So far, the best I could come up with is:

def chunk(string, size) (0..(string.length-1)/size).map{|i|string[i*size,size]} end >> chunk("abcdef",3) => ["abc", "def"] >> chunk("abcde",3) => ["abc", "de"] >> chunk("abc",3) => ["abc"] >> chunk("ab",3) => ["ab"] >> chunk("",3) => [] 

You might want chunk("", n) return [""] instead of [] . If so, just add this as the first line of the method:

 return [""] if string.empty? 

Would you recommend any better solution?

edit

Thanks to Jeremy Ruthen for this elegant and efficient solution: [edit: NOT effective!]

 def chunk(string, size) string.scan(/.{1,#{size}}/) end 

edit

The string.scan solution takes about 60 seconds to cut 512 thousand pieces into 1 thousand pieces 10,000 times compared to the original slicing solution, which takes just 2.4 seconds.

+82
string ruby chunking
Apr 16 '09 at 1:06
source share
7 answers

Use String#scan :

 >> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{4}/) => ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx"] >> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,4}/) => ["abcd", "efgh", "ijkl", "mnop", "qrst", "uvwx", "yz"] >> 'abcdefghijklmnopqrstuvwxyz'.scan(/.{1,3}/) => ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"] 
+148
Apr 16 '09 at 1:26
source share

Here is another way to do this:

 "abcdefghijklmnopqrstuvwxyz".chars.to_a.each_slice(3).to_a.map {|s| s.to_s } 

=> ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"]

+17
Feb 04 2018-11-21T00:
source share

I think this is the most efficient solution if you know that your string is a multiple of the block size

 def chunk(string, size) (string.length / size).times.collect { |i| string[i * size, size] } end 

and for parts

 def parts(string, count) size = string.length / count count.times.collect { |i| string[i * size, size] } end 
+5
Jul 26 '15 at 14:00
source share
 test.split(/(...)/).reject {|v| v.empty?} 

Deviation is necessary because it otherwise includes a space between sets. My regular expression is not quite right for me to figure out how to fix it right from my head.

+1
Apr 16 '09 at 1:20
source share

Here is another solution for a slightly different case where large strings are processed and there is no need to store all portions at a time. Thus, it stores one block at a time and works much faster than slicing lines:

 io = StringIO.new(string) until io.eof? chunk = io.read(chunk_size) do_something(chunk) end 
+1
Sep 20 '18 at 9:28
source share

The best solution that takes into account the last part of the line, which may be less than the size of the chunk:

 def chunk(inStr, sz) return [inStr] if inStr.length < sz m = inStr.length % sz # this is the last part of the string partial = (inStr.length / sz).times.collect { |i| inStr[i * sz, sz] } partial << inStr[-m..-1] if (m % sz != 0) # add the last part partial end 
+1
Feb 06 '19 at 10:19
source share

Are there any other limitations you have in mind? Otherwise, I would have been terribly tempted to do something simple, like

 [0..10].each { str[(i*w),w] } 
-one
Apr 16 '09 at 1:15
source share



All Articles