0

Taking the ruby API documentation explanation for String#each_char, it suggests that one receives a string when invoking this method together with a given block.

each_char {|cstr| block } → str

But when executing this in irb, the output is not as expected and only the string we started out with is returned.

irb(main):001:0> v = "foobar".each_char { |c| c + "." }
=> "foobar"

The problem this causes, is that assigning this method invocation is pretty deceiving, as it doesn't return the expected result, but merely the string we started with:

irb(main):002:0> puts v
foobar
=> nil

Using the break instruction is the only way to return a value from the block during the variable assignment.

irb(main):003:0> v = "foobar".each_char { |c| break c + "." }
=> "f."

Questions:

Why do methods invoked with a block not return the values they calculate? Is this just a special case with this implementation, as I'm sure Array#each does return a new Array. But then again, String#each_byte isn't returning something neither. What is going on here?

What is the most elegant way to assign the processed values from the block? Is monkey patching an option?

Additional infos: I'm using ruby 2.6.3

(Note: I've seen [this question from Ed] but while the conclusion in 2017 was that using the assignment operator leads to syntax errors, ruby clearly doesn't behave like this in the case of Array#each.)

6
  • Um, Array#each also returns self. Are you looking for the map/collect methods? Commented Feb 9, 2020 at 12:09
  • Although not related to the actual question, your example can be achieved with 'foobar'.gsub(/(?<=.)/m, '.') #=> "f.o.o.b.a.r.". Commented Feb 9, 2020 at 12:31
  • I suggest you remove all the tags except for “Ruby”, because it’s not likely that anyone would filter on any of the other tags, as they would know that they would miss many answers of interest by doing so. Suppose, for example, someone wanted to look at Ruby answers that used each. There might be thousands with the tag “Ruby” that do so, but yours might be the only one that also has the tag “each”. Commented Feb 9, 2020 at 16:36
  • I'm not really looking for map/collect or gsub. I just noticed that you can't assign the mutations done in the block of Array#each_char to a variable like the documentation suggests. The examples given here aren't really meant to achieve anything in particular. Commented Feb 10, 2020 at 9:29
  • "Why do methods invoked with a block not return the values they calculate?" - Some methods call the block more than once, e.g. each. Which of the values are they supposed to return, then? Commented Feb 10, 2020 at 19:53

1 Answer 1

1

First of all, #each not return a new array. Each means that it will process each element of an array, but in the end, it will return it's receiver.

To print something from the block you need to use print or puts.

There are a few ways to assign the result of each:

First: using #with_object

=> v = "foobar".each_char.with_object('') { |c, obj| obj << c + '.'  }
=> v
=> "f.o.o.b.a.r."

One more: using #chars and #join

=> v = "foobar".chars.join('.')
=> v
=> "f.o.o.b.a.r"
Sign up to request clarification or add additional context in comments.

3 Comments

so when #each return it's receiver, what dot #each_char (#each_byte) return? and what is the idea behind this inconsistency of returns?
@D.Asyl: each_char and each_byte return their receiver. All each-like methods always return their receiver. But actually, since the very purpose of each is only its side-effects, the return value doesn't even matter.
I could have sworn, that i tested the working of .each the other day and it returned indeed the expected array. It doesn't though, so my bad. There is nothing wrong then after all. This is ideed the solution. Thanks all.

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.