Related
Today I was surprised to find ruby automatically find the values of an array given as a block parameter.
For example:
foo = "foo"
bar = "bar"
p foo.chars.zip(bar.chars).map { |pair| pair }.first #=> ["f", "b"]
p foo.chars.zip(bar.chars).map { |a, b| "#{a},#{b}" }.first #=> "f,b"
p foo.chars.zip(bar.chars).map { |a, b,c| "#{a},#{b},#{c}" }.first #=> "f,b,"
I would have expected the last two examples to give some sort of error.
Is this an example of a more general concept in ruby?
I don't think my wording at the start of my question is correct, what do I call what is happening here?
Ruby block are quirky like that.
The rule is like this, if a block takes more than one argument and it is yielded a single object that responds to to_ary then that object is expanded. This makes yielding an array versus yielding a tuple seem to behave the same way for blocks that take two or more arguments.
yield [a,b] versus yield a,b do differ though when the block takes one argument only or when the block takes a variable number of arguments.
Let me demonstrate both of that
def yield_tuple
yield 1, 2, 3
end
yield_tuple { |*a| p a }
yield_tuple { |a| p [a] }
yield_tuple { |a, b| p [a, b] }
yield_tuple { |a, b, c| p [a, b, c] }
yield_tuple { |a, b, c, d| p [a, b, c, d] }
prints
[1, 2, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, nil]
Whereas
def yield_array
yield [1,2,3]
end
yield_array { |*a| p a }
yield_array { |a| p [a] }
yield_array { |a, b| p [a, b] }
yield_array { |a, b, c| p [a, b, c] }
yield_array { |a, b, c, d| p [a, b, c, d] }
prints
[[1, 2, 3]]
[[1, 2, 3]]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
And finally to show that everything in Ruby uses duck-typing
class A
def to_ary
[1,2,3]
end
end
def yield_arrayish
yield A.new
end
yield_arrayish { |*a| p a }
yield_arrayish { |a| p [a] }
yield_arrayish { |a, b| p [a, b] }
yield_arrayish { |a, b, c| p [a, b, c] }
yield_arrayish { |a, b, c, d| p [a, b, c, d] }
prints
[#<A:0x007fc3c2969190>]
[#<A:0x007fc3c2969050>]
[1, 2] # array expansion makes it look like a tuple
[1, 2, 3] # array expansion makes it look like a tuple
[1, 2, 3, nil] # array expansion makes it look like a tuple
PS, the same array expansion behavior applies for proc closures which behave like blocks, whereas lambda closures behave like methods.
Ruby's block mechanics have a quirk to them, that is if you're iterating over something that contains arrays you can expand them out into different variables:
[ %w[ a b ], %w[ c d ] ].each do |a, b|
puts 'a=%s b=%s' % [ a, b ]
end
This pattern is very useful when using Hash#each and you want to break out the key and value parts of the pair: each { |k,v| ... } is very common in Ruby code.
If your block takes more than one argument and the element being iterated is an array then it switches how the arguments are interpreted. You can always force-expand:
[ %w[ a b ], %w[ c d ] ].each do |(a, b)|
puts 'a=%s b=%s' % [ a, b ]
end
That's useful for cases where things are more complex:
[ %w[ a b ], %w[ c d ] ].each_with_index do |(a, b), i|
puts 'a=%s b=%s # %d' % [ a, b, i ]
end
Since in this case it's iterating over an array and another element that's tacked on, so each item is actually a tuple of the form %w[ a b ], 0 internally, which will be converted to an array if your block only accepts one argument.
This is much the same principle you can use when defining variables:
a, b = %w[ a b ]
a
# => 'a'
b
# => 'b'
That actually assigns independent values to a and b. Contrast with:
a, b = [ %w[ a b ] ]
a
# => [ 'a', 'b' ]
b
# => nil
I would have expected the last two examples to give some sort of error.
It does in fact work that way if you pass a proc from a method. Yielding to such a proc is much stricter – it checks its arity and doesn't attempt to convert an array argument to an argument list:
def m(a, b)
"#{a}-#{b}"
end
['a', 'b', 'c'].zip([0, 1, 2]).map(&method(:m))
#=> wrong number of arguments (given 1, expected 2) (ArgumentError)
This is because zip creates an array (of arrays) and map just yields each element, i.e.
yield ['a', 0]
yield ['b', 1]
yield ['c', 2]
each_with_index on the other hand works:
['a', 'b', 'c'].each_with_index.map(&method(:m))
#=> ["a-0", "b-1", "c-2"]
because it yields two separate values, the element and its index, i.e.
yield 'a', 0
yield 'b', 1
yield 'c', 2
Whenever I swap values in an array, I make sure I stored one of the values in a reference variable. But I found that Ruby can return two values as well as automatically swap two values. For example,
array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1]
I was wondering how Ruby does this.
Unlike other languages, the return value of any method call in Ruby is always an object. This is possible because, like everything in Ruby, nil itself is an object.
There's three basic patterns you'll see. Returning no particular value:
def nothing
end
nothing
# => nil
Returning a singular value:
def single
1
end
x = single
# => 1
This is in line with what you'd expect from other programming languages.
Things get a bit different when dealing with multiple return values. These need to be specified explicitly:
def multiple
return 1, 2
end
x = multiple
# => [ 1, 2 ]
x
# => [ 1, 2 ]
When making a call that returns multiple values, you can break them out into independent variables:
x, y = multiple
# => [ 1, 2 ]
x
# => 1
y
# => 2
This strategy also works for the sorts of substitution you're talking about:
a, b = 1, 2
# => [1, 2]
a, b = b, a
# => [2, 1]
a
# => 2
b
# => 1
No, Ruby doesn't actually support returning two objects. (BTW: you return objects, not variables. More precisely, you return pointers to objects.)
It does, however, support parallel assignment. If you have more than one object on the right-hand side of an assignment, the objects are collected into an Array:
foo = 1, 2, 3
# is the same as
foo = [1, 2, 3]
If you have more than one "target" (variable or setter method) on the left-hand side of an assignment, the variables get bound to elements of an Array on the right-hand side:
a, b, c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary[2]
If the right-hand side is not an Array, it will be converted to one using the to_ary method
a, b, c = not_an_ary
# is the same as
ary = not_an_ary.to_ary
a = ary[0]
b = ary[1]
c = ary[2]
And if we put the two together, we get that
a, b, c = d, e, f
# is the same as
ary = [d, e, f]
a = ary[0]
b = ary[1]
c = ary[2]
Related to this is the splat operator on the left-hand side of an assignment. It means "take all the left-over elements of the Array on the right-hand side":
a, b, *c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary.drop(2) # i.e. the rest of the Array
And last but not least, parallel assignments can be nested using parentheses:
a, (b, c), d = ary
# is the same as
a = ary[0]
b, c = ary[1]
d = ary[2]
# which is the same as
a = ary[0]
b = ary[1][0]
c = ary[1][1]
d = ary[2]
When you return from a method or next or break from a block, Ruby will treat this kind-of like the right-hand side of an assignment, so
return 1, 2
next 1, 2
break 1, 2
# is the same as
return [1, 2]
next [1, 2]
break [1, 2]
By the way, this also works in parameter lists of methods and blocks (with methods being more strict and blocks less strict):
def foo(a, (b, c), d) p a, b, c, d end
bar {|a, (b, c), d| p a, b, c, d }
Blocks being "less strict" is for example what makes Hash#each work. It actually yields a single two-element Array of key and value to the block, but we usually write
some_hash.each {|k, v| }
instead of
some_hash.each {|(k, v)| }
tadman and Jörg W Mittag know Ruby better than me, and their answers are not wrong, but I don't think they are answering what OP wanted to know. I think that the question was not clear though. In my understanding, what OP wanted to ask has nothing to do with returning multiple values.
The real question is, when you want to switch the values of two variables a and b (or two positions in an array as in the original question), why is it not necessary to use a temporal variable temp like:
a, b = :foo, :bar
temp = a
a = b
b = temp
but can be done directly like:
a, b = :foo, :bar
a, b = b, a
The answer is that in multiple assignment, the whole right hand side is evaluated prior to assignment of the whole left hand side, and it is not done one by one. So a, b = b, a is not equivalent to a = b; b = a.
First evaluating the whole right hand side before assignment is a necessity that follows from adjustment when the both sides of = have different numbers of terms, and Jörg W Mittag's description may be indirectly related to that, but that is not the main issue.
Arrays are a good option if you have only a few values. If you want multiple return values without having to know (and be confused by) the order of results, an alternative would be to return a Hash that contains whatever named values you want.
e.g.
def make_hash
x = 1
y = 2
{x: x, y: y}
end
hash = make_hash
# => {:x=>1, :y=>2}
hash[:x]
# => 1
hash[:y]
# => 2
Creating a hash as suggested by some is definitely better than array as array indexing can be confusing. When an additional attribute needs to be returned at a certain index, we'll need to make changes to all the places where the return value is used with array.
Another better way to do this is by using OpenStruct. Its advantage over using a hash is its ease of accessibility.
Example: computer = OpenStruct.new(ram: '4GB')
there are multiple ways to access the value of ram
as a symbol key: computer[:ram]
as a string key: computer['ram']
as an attribute(accessor method): computer.ram
Reference Article: https://medium.com/rubycademy/openstruct-in-ruby-ab6ba3aff9a4
I have two hashes:
g_record = {"4-2014"=>{:total_conversions=>0, :total_cost=>0.0}, "5-2014"=>{:total_conversions=>0, :total_cost=>189.8}, "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}
b_record = {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7}, "5-2014"=>{:total_conversions=>5, :total_cost=>298.03}}
My desired output is:
combined_record = {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7}, "5-2014"=>{:total_conversions=>5, :total_cost=>487.83}, "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}
I've had to combine hashes and sum values before and have been trying variations of
g_record.merge(b_record) {|a, b, c| b + c}
But this time I don't understand how I should get inside the intial hashes to run something like that. I feel like I should be able to do something like
g_record.merge(b_record).each {|e| e.merge {|a, b, c| b + c}
But I know that is incorrect.
I'd do :
merged_hash = g_record.merge(b_record) do |_, o, n|
o.merge(n) { |_, o1, n1| o1 + n1 }
end
merged_hash
# => {"4-2014"=>{:total_conversions=>7, :total_cost=>639.7},
# "5-2014"=>{:total_conversions=>5, :total_cost=>487.83},
# "6-2014"=>{:total_conversions=>0, :total_cost=>474.18}}
I have a model, FacebookProfile, that has three columns, a, b and c.
I invoke this object by doing:
#facebook_profile = FacebookProfile.find_by_facebook_id(1)
Then I can access each of the values for the columns a, b and c
#facebook_profile.a # result: 1
#facebook_profile.b # result: 2
#facebook_profile.c # result: 3
If I have an array of items a, b and c, how can I iterate through calling these on #facebook_profile?
My best guess:
my_array = ["a", "b", "c"]
for i in my_array
puts #facebook_profile.i
end
Expected output: 1, 2, 3
I have seen a way to iterate through all columns, but I have other columns besides a, b and c, so I don't think that will work.
I think you could use the sendoperator to do something along the lines of
my_array = ["a","b","c"]
for i in my_array
puts #facebook_profile.send(i.to_sym)
end
Is there a Ruby method similar to Haskell's cycle? Haskell's cycle takes a list and returns that list infinitely appended to itself. It's commonly used with take which grabs a certain number of elements off the top of an array. Is there a Ruby method that takes an array and returns the array appended to itself some n number of times?
Yes, it's called cycle. From the documentation:
Array.cycle
(from ruby core)
------------------------------------------------------------------------------
ary.cycle(n=nil) {|obj| block } -> nil
ary.cycle(n=nil) -> an_enumerator
------------------------------------------------------------------------------
Calls block for each element repeatedly n times or forever if none
or nil is given. If a non-positive number is given or the array is empty, does
nothing. Returns nil if the loop has finished without getting interrupted.
If no block is given, an enumerator is returned instead.
a = ["a", "b", "c"]
a.cycle {|x| puts x } # print, a, b, c, a, b, c,.. forever.
a.cycle(2) {|x| puts x } # print, a, b, c, a, b, c.
Edit:
It seems like whats inside the block is basically a "Lambda", and as far as I know, I can't make a lambda concat each element onto an existing array.
b = [1, 2, 3]
z = []
b.cycle(2) { |i| z << i }
z # => [1, 2, 3, 1, 2, 3]
You can multiply an array by an integer using Array#*:
ary * int → new_ary
[...] Otherwise, returns a new array built by concatenating the int copies of self.
So you can do things like this:
>> [1, 2] * 3
=> [1, 2, 1, 2, 1, 2]