2

i'm trying to loop through a Ruby string containing many lines using the each_line method, but I also want to change them. I'm using the following code, but it doesn't seem to work:

string.each_line{|line| line=change_line(line)}

I suppose, that Ruby is sending a copy of my line and not the line itself, but unfortunatelly there is no method each_line!. I also tried with the gsub! method, using /^.*$/ to detect each line, but it seems that it calls the change_line method only ones and replaces all lines with it. Any ideas how to do that? Thanks in advance :)

mjekov
  • 682
  • 2
  • 17
  • 32

5 Answers5

5

@azlisum: You are not storing the result of your concatenation. Use:

output = string.lines.map{|line|change_line(line)}.join

Comparing four ways to process by line in a string:

# Inject method (proposed by @steenslang)
output = string.each_line.inject(""){|s, line| s << change_line(line)}

# Join method (proposed by @Lars Haugseth)
output = string.lines.map{|line|change_line(line)}.join

# REGEX method (proposed by @olistik)
output = string.gsub!(/^(.*)$/) {|line| change_line(line)}

# String concatenation += method (proposed by @Erik Hinton)
output = "" 
string.each_line{|line| output += change_line(line)}                   

The timing with Benchmark:

                   user     system      total        real
Inject Time:   7.920000   0.010000   7.930000 (  7.920128)
Join Time:     7.150000   0.010000   7.160000 (  7.155957)
REGEX Time:   11.660000   0.010000  11.670000 ( 11.661059)
+= Time:       7.080000   0.010000   7.090000 (  7.076423)

As @steenslag pointed out, 's += a' will generate a new string for each concatenation and is therefor not usually the best choice.

So given that, and given the times, your best bet is:

output = string.lines.map{|line|change_line(line)}.join

Also, this is the cleaner looking choice IMHO.

Notes:
Using Benchmark
Ruby-Doc: Benchmark

Community
  • 1
  • 1
Sean Vikoren
  • 1,084
  • 1
  • 13
  • 24
3

You should try starting out with a blank string too, each_lining through the string and then pushing the results onto the blank string.

output = "" 
string.each_line{|line| output += change_line(line)}

In your original example, you are correct. Your changes are occuring but they are not being ssved anywhere. Each in Ruby does not alter anything by default.

Erik Hinton
  • 1,948
  • 10
  • 15
2

You could use gsub! passing a block to it:

string.gsub!(/^(.*)$/) {|line| change_line(line)}

source: String#gsub!

olistik
  • 784
  • 5
  • 13
1

String#each_line is meant for reading lines in a string, not writing them. You can use this to get the result you want like so:

changed_string = ""
string.each_line{ |line| changed_string += change_line(line) }
Carl Zulauf
  • 39,378
  • 2
  • 34
  • 47
0

If you don't give each_line a block, you'll get an enumerator, which has the inject method.

str = <<HERE
smestring dsfg
line 2
HERE

res = str.each_line.inject(""){|m,line|m << line.upcase}
steenslag
  • 79,051
  • 16
  • 138
  • 171