ruby nested for loops - ruby

This might be a stupid question, but I am attempting to learn the basic's of Ruby so please be gentle. I would like to iterate thru an array of strings and then thru each letter in that string.
groceries = ['turkey', 'cranberries', 'stuffing', 'pumkin pie', 'whip-cream']
for i in groceries do
p i
for x in i do
p x
end
end
When I attempt to run this I get the following error. Any assistance would be awesome.
undefined method `each' for "turkey":String (NoMethodError)

As mu is too short said each seems to be more aligned with standard Ruby convention. That said, I'm a novice with Ruby and programming in general. So take my opinions with a grain of salt.
At any rate, there are a few useful methods that seem to fit your need. each_char and chars will iterate through a string.
for i in groceries do
p i
i.chars { |c| p c }
end
Or,
groceries.each do |i|
p i
i.chars { |c| p c }
end

Using Array#map and String#each_char
For-loops are syntactic sugar in Ruby, and you generally won't see them in idiomatic Ruby code. Instead, Rubyists tend to use iterator methods. The following is more idiomatic, although there are certainly other ways to do what you are looking to accomplish.
groceries.map { |item| p item; item.each_char { |char| p char } }
In addition to printing the output you wanted, this has the benefit of returning your original Array of String values as well:
#=> ["turkey", "cranberries", "stuffing", "pumkin pie", "whip-cream"]
This works on any version of Ruby >= 2.7.1 (possibly earlier, but I didn't bother to test). You may be able to take further shortcuts using features from later Ruby versions, but this should certainly get you started.

Related

Why won't my Ruby for-loop iterate over a String in my palindrome method?

I am having trouble with getting my for-loop to process a string. It's just a simple method to tell whether or not a word is a palindrome (a word that is spelled that same way backwards and forwards). I have tweaked the for-loop multiple times but keep getting the same error message below. Could anyone point me in the right direction?
Code:
def palindrome?(string)
string2 = ""
for i in string
string2 = string[i] + string2
end
if string2 == string1
return true
end
end
palindrome?("abcba")
Error:
hours.rb:7:in `palindrome?': undefined method `each' for 5:Fixnum (NoMethodError)
from hours.rb:17:in `<main>'
The problem is that you can't iterate over a string (like you can for example in Python). You'll first need to convert it to an Array with .split:
for c in string.split
string2 = c + string2
end
That being said, you shouldn't use for loops in Ruby. They're translated internally to an each method, hence your confusing error. It's better to just write each from the get-go:
string.split.each do |c|
string2 = c + string2
end
No Ruby programmer would ever use for in any circumstance, it's only used by people new to Ruby ;-)
Note that the Array.each is just one iteration method; for example there's also the String.each_char method:
string.each_char do |c|
string2 = c + string2
end
Lastly, your code is not correct in several other locations. I'm not going to point out all these errors to you, as it will be much more beneficial and educational for you if you solve this programming exercise yourself ;-)
TL;DR
Aside from being inefficient, your code doesn't work because a String is not an Array, nor does it mix in Enumerator to provide an #each method.
While the String#[] method allows indexing into the string, there is no String#each method to invoke. As a result, you can't use a String object in a Ruby for-loop because it's just syntactic sugar for #each.
Understanding the Exception
I'm not sure what version of Ruby you're running, but the exception you've listed in your post is not reproducible on my system. When running on Ruby 2.3.1, the code generates a fairly clear exception:
for i in string
string2 = string[i] + string2
end
NoMethodError: undefined method `each' for "abcba":String
This is pretty straighforward. It tells you that String has no #each method, which is what the syntactic sugar of for i in string is really invoking under the hood. If you want to iterate, you need some form of Enumerator or Enumerable to work with.
Iterating Over a String
The String class has a number of useful methods for converting a string to an iterable object. Some examples include:
String#each_char can be passed a block, or return an Enumerator
String#chars returns an Array
String#split also returns an Array (e.g. 'abcba'.split //)
For example, your code could be refactored to use a block like so:
string = 'abcba'
tmpstr = ''
string.each_char { |char| tmpstr < char; puts true if tmpstr == 'abcba' }
#=> "abcba"
However, while this highlights how to solve for your exception, it is still needlessly complex and inefficient.
Leveraging Built-In Methods
Unless you're doing this for homework, the right way to do this in Ruby is to leverage the built-in methods that operate at C speeds and don't create temporary Ruby objects that need to be garbage-collected later. For example, to test whether a given string reads the same backwards or forwards, you can simply compare a reversed string to the original using the String#reverse and String#eql? methods.
def palindrome? str
str.reverse.eql? str
end
palindrome? 'abcba'
#=> true
palindrome? 'abcde'
#=> false
You can also use String#== instead of #eql? if you prefer, but I think using the latter is clearer in this case. The method chain makes it clear that you're invoking a String method rather than a bit of language syntax for the comparison. That distinction can be a real help when learning the ins and outs of Ruby's core, but in this case the result will be the same either way.
As Carpetsmoker pointed out, you can't iterate directly over a string. However, Ruby provides both positive and negative indexing for elements. Negative indices are located relative to the end of the array or string. This allows you to do your checking quite efficiently, and short circuit your testing as soon as you identify you don't have a palindrome:
def palindrome?(str)
(0...str.length/2).all? { |i| str[i] == str[-(i+1)] }
end
If you want to be more object-oriented about it, you can convert it to a method in class String:
class String
def palindrome?
(0...length/2).all? { |i| self[i] == self[-(i+1)] }
end
end
p "abcba".palindrome? # => true
Note — Edited to utilize Cary Swoveland's excellent suggestion about using all? rather than an explicit return from the block. This makes it a one-liner.
what you're looking for is:
def palindrome?(string)
string2 = ""
for i in 0...string.length
string2 = string[i] + string2
end
if string2 == string
return true
end
end
Note you could define it simpler:
def palindrome?(string)
string == string.reverse
end
You can write it like this:
def palindrome?(str)
str == str.reverse
end

Is there a { |x| x } shorthand in ruby?

I often use .group_by{ |x| x } and .find{ |x| x }
The latter is to find the first item in an array which is true.
Currently I'm just using .compact.first but I feel like there must be an elegant way to use find here, like find(&:to_bool) or .find(true) that I'm missing.
Using .find(&:nil?) works but is the opposite of what I want, and I couldn't find a method that was the opposite of #find or #detect, or a method like #true?
So is there a more elegant way to write .find{ |x| x }? If not, I'll stick with .compact.first
(I know compact won't remove false but that's not a problem for me, also please avoid rails methods for this)
Edit: For my exact case it is used on arrays of only strings and nils e.g.
[nil, "x", nil, nil, nil, nil, "y", nil, nil, nil, nil] => "x"
If you do not care about what is returned you can sometimes use the hash method.
Thw feature you are asking for is not available in Ruby yet, however. it is present in the Ruby road-map:
https://bugs.ruby-lang.org/issues/6373
Expected to be implemented before 2035-12-25, can you wait?
That being said, how much typing is group_by{|x|x} ?
Edit:
As Stefan pointed out, my answer is now longer valid for Ruby 2.2 and above since the introduction of Object#itself.
There’s not.
If tap worked without a block you could do:
array.detect(&:tap)
But it doesn’t. Either way, I think what you have is extremely concise, idiomatic, and happens to be the same number of characters as the non-working above alternative, and thus you should stick with that:
array.compact.first
You could monkey-patch your way to getting a shorter version, but then it becomes unclear to anyone otherwise familiar with Ruby, which probably isn’t worth the minor “savings”.
As a curiosity, if you happened to want array.detect { |x| !x } (the opposite) you could do:
array.detect(&:!)
This works because !x is actually shorthand for x.!. Of course this would only ever give you nil or false, which is probably not very useful.
No, there is not. I personally have a utility library I include in all my projects which has something like
IDENTITIY = -> x { x }
Then you would have
.group_by(&IDENTITY)
There is also Object#itself that simply returns self:
.group_by(&:itself)
Although the tag is for ruby - with Rails (more specifically ActiveSupport) you are given a method presence which will work for anything that responds positively to present? (that would exclude blank strings, arrays, hashes, etc):
array.find(&:presence)
It's not quite equivalent to the preferred result, but it will work for most cases I've come across.
I frequently use group_by, map, select, sort_by, and other various hash methods. I discovered this useful little extension yesterday by fiddling around with another answer on a similar question:
class Hash
def method_missing(n)
if has_key? n
self[n]
else
raise NoMethodError
end
end
end
For any hash created by ruby, or any data that has been jsonified by as_json, this addition allows me to write code which is a little shorter. Example:
# make yellow cells
yellow = red = false
tube_steps_status.group_by(&:step_ordinal).each do |type|
group = type.last.select(&:completed).sort_by(&:completed)
red = true if group.last.step_status == 'red' if group.any?
yellow = true if group.map(&:step_status).include?('red')
end
tube_summary_status = 'yellow' if yellow unless red

Using ruby-debug in for i in 0...5

I am learning ruby from 'Programming ruby 1.9'. I am learning to use the ruby-debug so I can understand what is going on underneath. I use rubymine since it integrates ruby-debug19 or something like that (it says I don't have the gem and installs it). Here is the question, I was able to step through the code and explore the variables and the stack. However, when it reaches a for i in 0...5, the debugger says
stack frame not available
I know that ruby don't use for loops much but I'd still like to know if there debug through for loops.
Code:
raw_text = %{
The problem breaks down into two parts. First, given some text as a
string, return a list of words. That sounds like an array. Then, build a
count for each distinct word. That sounds like a use for a hash---we can
index it with the word and use the corresponding entry to keep a count.}
word_list = words_from_string(raw_text)
counts = count_frequency(word_list)
sorted = counts.sort_by {|word, count| count}
top_five = sorted.last(5)
for i in 0...5 # (this is ugly code--read on
word = top_five[i][0] # for a better version)
count = top_five[i][1]
puts "#{word}: #{count}"
end
If you take a look at the Ruby Language Specification (clause 11.5.2.3.4 on p. 91), you will see that
for i in 0...5
word = top_five[i][0]
count = top_five[i][1]
puts "#{word}: #{count}"
end
is syntactic sugar for
(0...5).each do |i|
word = top_five[i][0]
count = top_five[i][1]
puts "#{word}: #{count}"
end
except that no new variable scope is created for the block. So, the code with for will be translated into the code with each and executed as if it were written that way, except that the variables used in the for loop leak into the surrounding scope.
To put it another way: for actually executes each but without allocating a new stack frame for the block. So, the error message is exactly right: there is a call to a block, but somehow there is no stack frame allocated for that block call. That obviously confuses the debugger.
Now, one might argue that this is a bug and that for loops should get special treatment inside the debugger. I guess that so far nobody has ever bothered to fix that bug, since nobody ever uses for loops, precisely because they leak their variables into the surrounding scope and are exactly equivalent to an idiomatic each which doesn't.
What do I mean by "leaking variables"? See here:
(1..2).each do |i|
t = true
end
i
# NameError: undefined local variable or method `i' for main:Object
t
# NameError: undefined local variable or method `t' for main:Object
for i in 1..2
t = true
end
i
# => 2
t
# => true

Ruby while syntax

Does anybody why I can write this:
ruby-1.8.7-p302 > a = %w( a b c)
=> ["a", "b", "c"]
ruby-1.8.7-p302 > while (i = a.shift) do; puts i ; end
a
b
c
=> nil
Which looks like passing a block to while.
And not:
while(i = a.shift) { puts i; }
Is it because the "do" of the while syntax is just syntaxic sugar and as nothing to do with the "do" of a block?
Is it because the do of the while syntax is just syntaxic sugar and as nothing to do with the do of a block?
More or less, yes. It's not syntactic sugar, it's simply a built-in language construct, like def or class, as #meagar already wrote.
It has nothing to do with the do of a block, except that keywords are expensive and so reusing keywords makes sense. (By "expensive" I mean that they limit the programmer in his expressiveness.)
In a while loop, there are two ways to separate the block from the condition:
the do keyword and
an expression separator.
There are, in turn, two different expression separators in Ruby:
the semicolon ; and
a newline
So, all three of the following are valid:
while i = a.shift do puts i end # do
while i = a.shift; puts i end # semicolon
while i = a.shift
puts i end # newline
[Obviously, that last one wouldn't be written that way, you would put the end on a new line, dedented to match the while. I just wanted to demonstrate what is the minimum needed to separate the parts of the while loop.]
By the way: it is highly un-idiomatic to put the condition in parentheses. There's also a lot of superfluous semicolons in your code. And the variable name i is usually reserved for an index, not an element. (I normally use el for generic elements, but I much prefer more semantic names.)
It is also highly un-idiomatic to iterate a collection manually. Your code would be much better written as
a.each(&method(:puts)).clear
Not only is it much easier to understand what this does (print all elements of the array and delete all items from it), it is also much easier to write (there is no way to get the termination condition wrong, or screw up any assignments). It also happens to be more efficient: your version is Θ(n2), this one is Θ(n).
And actually, that's not really how you would write it, either, because Kernel#puts already implements that behavior, anyway. So, what you would really write is this
puts a
a.clear
or maybe this
a.tap(&method(:puts)).clear
[Note: this very last one is not 100% equivalent. It prints a newline for an empty array, all the other ones print nothing.]
Simple. Clear. Concise. Expressive. Fast.
Compare that to:
while (i = a.shift) do; puts i ; end
I actually had to run that multiple times to be 100% clear what it does.
while doesn't take a block, it's a language construct. The do is optional:
while (i = a.shift)
puts i
end

What is the "right" way to iterate through an array in Ruby?

PHP, for all its warts, is pretty good on this count. There's no difference between an array and a hash (maybe I'm naive, but this seems obviously right to me), and to iterate through either you just do
foreach (array/hash as $key => $value)
In Ruby there are a bunch of ways to do this sort of thing:
array.length.times do |i|
end
array.each
array.each_index
for i in array
Hashes make more sense, since I just always use
hash.each do |key, value|
Why can't I do this for arrays? If I want to remember just one method, I guess I can use each_index (since it makes both the index and value available), but it's annoying to have to do array[index] instead of just value.
Oh right, I forgot about array.each_with_index. However, this one sucks because it goes |value, key| and hash.each goes |key, value|! Is this not insane?
This will iterate through all the elements:
array = [1, 2, 3, 4, 5, 6]
array.each { |x| puts x }
# Output:
1
2
3
4
5
6
This will iterate through all the elements giving you the value and the index:
array = ["A", "B", "C"]
array.each_with_index {|val, index| puts "#{val} => #{index}" }
# Output:
A => 0
B => 1
C => 2
I'm not quite sure from your question which one you are looking for.
I think there is no one right way. There are a lot of different ways to iterate, and each has its own niche.
each is sufficient for many usages, since I don't often care about the indexes.
each_ with _index acts like Hash#each - you get the value and the index.
each_index - just the indexes. I don't use this one often. Equivalent to "length.times".
map is another way to iterate, useful when you want to transform one array into another.
select is the iterator to use when you want to choose a subset.
inject is useful for generating sums or products, or collecting a single result.
It may seem like a lot to remember, but don't worry, you can get by without knowing all of them. But as you start to learn and use the different methods, your code will become cleaner and clearer, and you'll be on your way to Ruby mastery.
I'm not saying that Array -> |value,index| and Hash -> |key,value| is not insane (see Horace Loeb's comment), but I am saying that there is a sane way to expect this arrangement.
When I am dealing with arrays, I am focused on the elements in the array (not the index because the index is transitory). The method is each with index, i.e. each+index, or |each,index|, or |value,index|. This is also consistent with the index being viewed as an optional argument, e.g. |value| is equivalent to |value,index=nil| which is consistent with |value,index|.
When I am dealing with hashes, I am often more focused on the keys than the values, and I am usually dealing with keys and values in that order, either key => value or hash[key] = value.
If you want duck-typing, then either explicitly use a defined method as Brent Longborough showed, or an implicit method as maxhawkins showed.
Ruby is all about accommodating the language to suit the programmer, not about the programmer accommodating to suit the language. This is why there are so many ways. There are so many ways to think about something. In Ruby, you choose the closest and the rest of the code usually falls out extremely neatly and concisely.
As for the original question, "What is the “right” way to iterate through an array in Ruby?", well, I think the core way (i.e. without powerful syntactic sugar or object oriented power) is to do:
for index in 0 ... array.size
puts "array[#{index}] = #{array[index].inspect}"
end
But Ruby is all about powerful syntactic sugar and object oriented power, but anyway here is the equivalent for hashes, and the keys can be ordered or not:
for key in hash.keys.sort
puts "hash[#{key.inspect}] = #{hash[key].inspect}"
end
So, my answer is, "The “right” way to iterate through an array in Ruby depends on you (i.e. the programmer or the programming team) and the project.". The better Ruby programmer makes the better choice (of which syntactic power and/or which object oriented approach). The better Ruby programmer continues to look for more ways.
Now, I want to ask another question, "What is the “right” way to iterate through a Range in Ruby backwards?"! (This question is how I came to this page.)
It is nice to do (for the forwards):
(1..10).each{|i| puts "i=#{i}" }
but I don't like to do (for the backwards):
(1..10).to_a.reverse.each{|i| puts "i=#{i}" }
Well, I don't actually mind doing that too much, but when I am teaching going backwards, I want to show my students a nice symmetry (i.e. with minimal difference, e.g. only adding a reverse, or a step -1, but without modifying anything else).
You can do (for symmetry):
(a=*1..10).each{|i| puts "i=#{i}" }
and
(a=*1..10).reverse.each{|i| puts "i=#{i}" }
which I don't like much, but you can't do
(*1..10).each{|i| puts "i=#{i}" }
(*1..10).reverse.each{|i| puts "i=#{i}" }
#
(1..10).step(1){|i| puts "i=#{i}" }
(1..10).step(-1){|i| puts "i=#{i}" }
#
(1..10).each{|i| puts "i=#{i}" }
(10..1).each{|i| puts "i=#{i}" } # I don't want this though. It's dangerous
You could ultimately do
class Range
def each_reverse(&block)
self.to_a.reverse.each(&block)
end
end
but I want to teach pure Ruby rather than object oriented approaches (just yet). I would like to iterate backwards:
without creating an array (consider 0..1000000000)
working for any Range (e.g. Strings, not just Integers)
without using any extra object oriented power (i.e. no class modification)
I believe this is impossible without defining a pred method, which means modifying the Range class to use it. If you can do this please let me know, otherwise confirmation of impossibility would be appreciated though it would be disappointing. Perhaps Ruby 1.9 addresses this.
(Thanks for your time in reading this.)
Use each_with_index when you need both.
ary.each_with_index { |val, idx| # ...
The other answers are just fine, but I wanted to point out one other peripheral thing: Arrays are ordered, whereas Hashes are not in 1.8. (In Ruby 1.9, Hashes are ordered by insertion order of keys.) So it wouldn't make sense prior to 1.9 to iterate over a Hash in the same way/sequence as Arrays, which have always had a definite ordering. I don't know what the default order is for PHP associative arrays (apparently my google fu isn't strong enough to figure that out, either), but I don't know how you can consider regular PHP arrays and PHP associative arrays to be "the same" in this context, since the order for associative arrays seems undefined.
As such, the Ruby way seems more clear and intuitive to me. :)
Here are the four options listed in your question, arranged by freedom of control. You might want to use a different one depending on what you need.
Simply go through values:
array.each
Simply go through indices:
array.each_index
Go through indices + index variable:
for i in array
Control loop count + index variable:
array.length.times do | i |
Trying to do the same thing consistently with arrays and hashes might just be a code smell, but, at the risk of my being branded as a codorous half-monkey-patcher, if you're looking for consistent behaviour, would this do the trick?:
class Hash
def each_pairwise
self.each { | x, y |
yield [x, y]
}
end
end
class Array
def each_pairwise
self.each_with_index { | x, y |
yield [y, x]
}
end
end
["a","b","c"].each_pairwise { |x,y|
puts "#{x} => #{y}"
}
{"a" => "Aardvark","b" => "Bogle","c" => "Catastrophe"}.each_pairwise { |x,y|
puts "#{x} => #{y}"
}
I'd been trying to build a menu (in Camping and Markaby) using a hash.
Each item has 2 elements: a menu label and a URL, so a hash seemed right, but the '/' URL for 'Home' always appeared last (as you'd expect for a hash), so menu items appeared in the wrong order.
Using an array with each_slice does the job:
['Home', '/', 'Page two', 'two', 'Test', 'test'].each_slice(2) do|label,link|
li {a label, :href => link}
end
Adding extra values for each menu item (e.g. like a CSS ID name) just means increasing the slice value. So, like a hash but with groups consisting of any number of items. Perfect.
So this is just to say thanks for inadvertently hinting at a solution!
Obvious, but worth stating: I suggest checking if the length of the array is divisible by the slice value.
If you use the enumerable mixin (as Rails does) you can do something similar to the php snippet listed. Just use the each_slice method and flatten the hash.
require 'enumerator'
['a',1,'b',2].to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }
# is equivalent to...
{'a'=>1,'b'=>2}.to_a.flatten.each_slice(2) {|x,y| puts "#{x} => #{y}" }
Less monkey-patching required.
However, this does cause problems when you have a recursive array or a hash with array values. In ruby 1.9 this problem is solved with a parameter to the flatten method that specifies how deep to recurse.
# Ruby 1.8
[1,2,[1,2,3]].flatten
=> [1,2,1,2,3]
# Ruby 1.9
[1,2,[1,2,3]].flatten(0)
=> [1,2,[1,2,3]]
As for the question of whether this is a code smell, I'm not sure. Usually when I have to bend over backwards to iterate over something I step back and realize I'm attacking the problem wrong.
In Ruby 2.1, each_with_index method is removed.
Instead you can use each_index
Example:
a = [ "a", "b", "c" ]
a.each_index {|x| print x, " -- " }
produces:
0 -- 1 -- 2 --
The right way is the one you feel most comfortable with and which does what you want it to do. In programming there is rarely one 'correct' way to do things, more often there are multiple ways to choose.
If you are comfortable with certain way of doings things, do just it, unless it doesn't work - then it is time to find better way.
Using the same method for iterating through both arrays and hashes makes sense, for example to process nested hash-and-array structures often resulting from parsers, from reading JSON files etc..
One clever way that has not yet been mentioned is how it's done in the Ruby Facets library of standard library extensions. From here:
class Array
# Iterate over index and value. The intention of this
# method is to provide polymorphism with Hash.
#
def each_pair #:yield:
each_with_index {|e, i| yield(i,e) }
end
end
There is already Hash#each_pair, an alias of Hash#each. So after this patch, we also have Array#each_pair and can use it interchangeably to iterate through both Hashes and Arrays. This fixes the OP's observed insanity that Array#each_with_index has the block arguments reversed compared to Hash#each. Example usage:
my_array = ['Hello', 'World', '!']
my_array.each_pair { |key, value| pp "#{key}, #{value}" }
# result:
"0, Hello"
"1, World"
"2, !"
my_hash = { '0' => 'Hello', '1' => 'World', '2' => '!' }
my_hash.each_pair { |key, value| pp "#{key}, #{value}" }
# result:
"0, Hello"
"1, World"
"2, !"

Resources