Sometimes I need to deal with key / value data.
I dislike using Arrays, because they are not constrained in size (it's too easy to accidentally add more than 2 items, plus you end up needing to validate size later on). Furthermore, indexes of 0 and 1 become magic numbers and do a poor job of conveying meaning ("When I say 0, I really mean head...").
Hashes are also not appropriate, as it is possible to accidentally add an extra entry.
I wrote the following class to solve the problem:
class Pair
attr_accessor :head, :tail
def initialize(h, t)
#head, #tail = h, t
end
end
It works great and solves the problem, but I am curious to know: does the Ruby standard library comes with such a class already?
No, Ruby doesn't have a standard Pair class.
You could take a look at "Using Tuples in Ruby?".
The solutions involve either using a similar class as yours, the Tuples gem or OpenStruct.
Python has tuple, but even Java doesn't have one: "A Java collection of value pairs? (tuples?)".
You can also use OpenStruct datatype. Probably not exactly what you wanted, but here is an implementation ...
require 'ostruct'
foo = OpenStruct.new
foo.head = "cabeza"
foo.tail = "cola"
Finally,
puts foo.head
=> "cabeza"
puts foo.tail
=> "cola"
No, there is no such class in the Ruby core library or standard libraries. It would be nice to have core library support (as well as literal syntax) for tuples, though.
I once experimented with a class very similar to yours, in order to replace the array that gets yielded by Hash#each with a pair. I found that monkey-patching Hash#each to return a pair instead of an array actually breaks surprisingly little code, provided that the pair class responds appropriately to to_a and to_ary:
class Pair
attr_reader :first, :second
def to_ary; [first, second] end
alias_method :to_a, :to_ary
private
attr_writer :first, :second
def initialize(first, second)
self.first, self.second = first, second
end
class << self; alias_method :[], :new end
end
You don't need a special type, you can use a 2-element array with a little helper to give the pairs a consistent order. E.g.:
def pair(a, b)
(a.hash < b.hash) ? [a, b] : [b, a]
end
distances = {
pair("Los Angeles", "New York") => 2_789.6,
pair("Los Angeles", "Sydney") => 7_497,
}
distances[ pair("Los Angeles", "New York") ] # => 2789.6
distances[ pair("New York", "Los Angeles") ] # => 2789.6
distances[ pair("Sydney", "Los Angeles") ] # => 7497
I have this hash $chicken_parts, which consists of symbol/hash pairs (many more than shown here):
$chicken_parts = { :beak = > {"name"=>"Beak", "color"=>"Yellowish orange", "function"=>"Pecking"}, :claws => {"name"=>"Claws", "color"=>"Dirty", function"=>"Scratching"} }
Then I have a class Embryo which has two class-specific hashes:
class Embryo
#parts_grown = Hash.new
#currently_developing = Hash.new
Over time, new pairs from $chicken_parts will be .merge!ed into #parts_grown. At various times, #currently developing will be declared equal to one of the symbol/hash pairs from #parts_grown.
I'm creating Embryo class functions and I want to be able to access the "name", "color", and "function" values in #currently_developing, but I don't seem to be able to do it.
def grow_part(part)
#parts_grown.merge!($chicken_parts[part])
end
def develop_part(part)
#currently_developing = #parts_grown[part]
seems to populate the hashes as expected, but
puts #currently_developing["name"]
does not work. Is this whole scheme a bad idea? Should I just make the Embryo hashes into arrays of symbols from $chicken_parts, and refer to it whenever needed? That seemed like cheating to me for some reason...
There's a little bit of confusion here. When you merge! in grow_part, you aren't adding a :beak => {etc...} pair to #parts_grown. Rather, you are merging the hash that is pointed too by the part name, and adding all of the fields of that hash directly to #parts_grown. So after one grow_part, #parts_grown might look like this:
{"name"=>"Beak", "color"=>"Yellowish orange", "function"=>"Pecking"}
I don't think that's what you want. Instead, try this for grow_part:
def grow_part(part)
#parts_grown[part] = $chicken_parts[part]
end
class Embryo
#parts_grown = {a: 1, b: 2}
def show
p #parts_grown
end
def self.show
p #parts_grown
end
end
embryo = Embryo.new
embryo.show
Embryo.show
--output:--
nil
{:a=>1, :b=>2}
I have a bunch of functions that take in either an array of objects, or a single object designed to be treated as an array containing one object, and I am looking for a cleaner way to accomplish this. Basically, I want to know how I could make the unless part in the following function more concise:
def foo(bar_or_bars)
unless bar_or_bars.is_a?(Array)
bar_or_bars = [bar_or_bars]
end
bar_or_bars.each { |baz| ... }
end
Any help will be appreciated! Thanks.
The simplest solution is to use Kernel method Array:
Array(5) #=> [5]
Array([1, 2, 3]) #=> [1,2,3]
so
def foo(bar_or_bars)
Array(bar_or_bars).each { |bar| ... }
This will even work on nested arrays that have arrays as elements (they wont be flattened out).
One case this won't work for is Hashes:
Array(a: 1, b: 2) #=> [[:a, 1], [:b, 2]]
If you want to iterate through unchanged Hash objects, best use Array.wrap from ActiveSupport.
First thing you could do is to write the unless logic in a single line:
bars = bar_or_bars.is_a?(Array) ? bar_or_bars : [bar_or_bars]
As you see, I give it a new name here, as it's no longer a bar or bars, it's now definitely a collection.
The problem with this and your original approach is that although your function could work on any Enumerable, you will force your users to give you an argument of a specific type, which breaks duck typing.
A neat trick to partially solve that issue is the following:
def foo(bar_or_bars)
bars = [*bar_or_bars]
bars.each { |baz| ... }
end
I wouldn't exactly call that readable, though. It actually smells a lot like bad API design. Probably you should better take multiple arguments like this:
def foo(*bars)
bars.each { |baz| ... }
end
And let the caller decide whether he wants to pass a single object or an array:
foo("XYZ")
ary = ["abc", "def"]
foo(*ary)
I am currently using this:
bars = [bar_or_bars].flatten
I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.
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, !"