How would I create three empty hashes with a single line of code?
I know that a = b = c = Hash.new won't work, since that'll create three references to the same Hash object.
a,b,c = Hash.new will assign the Hash to a, but b and c remain nil.
I know I could do a, b, c = Hash.new, Hash.new, Hash.new, but that doesn't look very DRY.
As I posted as a comment, I think a, b, c = {}, {}, {} is the best way, because it's short, and easy to read. If you really want to do it in a more complicated way, something like this will work:
>> a, b, c = Array.new(3) { Hash.new } #=> [{}, {}, {}]
>> a #=> {}
>> b #=> {}
>> c #=> {}
I am not really sure if I would use that, but it is possible:
a, b, c = 3.times.map { Hash.new }
# or
a, b, c = (1..3).map { Hash.new }
Although you already marked an answer, I'd throw in another way which I find as the simplest one:
a,b,c = [{}]*3
I am not really sure if I would use that, but it is possible:
a, b, c = 3.times.map { Hash.new }
#or
a, b, c = (1..3).map { Hash.new }
And yet another answer.. since you can simply use {} instead of Hash.new
The assignation could be like this:
a, b, c = 3.times.map{{}}
Related
I am trying to generate array as a result of comparison 2 arrays as:
a = %w{a b a e}
b = %w{c d a e}
After comparing it should give result as:
c = [false, false, true, true]
Is there any ruby way which is better than doing a for loop because I have to do this comparison with a lot of arrays.
This way?
a.zip(b).map { |a, b| a == b }
Uglier, but just to show an alternative:
a.map.with_index { |aa, i| aa == b[i] }
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 want to know how I can declare multiple variables. I typed a,b=1 expecting to get a=1,b=1, but I got:
a,b=1
a #=> 1
b #=> nil
How am I able to do this?
After this code, I did:
a="Hello "
b=a
c="World~"
b << c
b #=> "Hello World"
Why is b the same as a's value?
To declare multiple vars on the same line, you can do that:
a = b = "foo"
puts a # return "foo"
puts b # return "foo" too
About your second question, when doing b << c, you are assigning c's value to b. Then, you are overriding previous value stored in b. Meanwhile, a keeps the same value because Ruby does not user pointers.
What you are doing is called destructuring assignment. Basically, you take what is on the right side of the equals sign, and destructure it, or break it apart, and then assign each section to each corresponding variable on the left.
Ruby is super friendly, and is providing some syntactic sugar that might be confusing.
When you type this:
a, b = 1
You are really saying something closer to this:
[a, b] = [1, nil]
A good example of destructuring assignment can be found here. It's for JavaScript, but I like it because the syntax is very explicit about what is happen when you do such an assigment.
I suppose, in the case of
a, b, c = 1, 2
the runtime system works the following way:
a, b, c = [1, 2]
_result = ( a, b, c = (_values = [1, 2]) )
a = _values[0] # => 1
b = _values[1] # => 2
c = _values[2] # => nil
_result = _values # => [1, 2]
However, in the case of a single value on the right hand side: a, b = 1, the computation process looks a bit different:
_result = ( a, b = ( _value = (_values = [1]).first ) )
a = _values[0] # => 1
b = _values[1] # => nil
_result = _value # => 1
Can someone approve or disprove my assumption?
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}}
look this class:
class Test
def initialize a, b, c
#a = a, #b = b, #c = c
end
end
class AnotherTest
def initialize a, b, c
#a = a
#b = b
#c = c
end
end
array = []
array.push Test.new "a1" ,"b1", "c1"
array.push AnotherTest.new "a2" ,"b2", "c2"
p array
I think this should be the same,but not:
<Test:0x000000022aba78 #b="b1", #c="c1", #a=["a1", "b1", "c1"]>
<AnotherTest:0x000000022ab9b0 #a="a2", #b="b2", #c="c2">]
Anybody who can give me an explain?
If you try in irb this expression:
a = "something" #=> "something"
As you can see, the assignment operation returns the result, because in ruby every expression should return something. So then this expression:
#b = b #=> b
will return the value of #b. Then in this expression
#a = a, #b = b, #c = c
where #b = b and #c = c will evaluate to b and c
So finally we will have this expression:
#a = a, b, c
And as you know it's another form for initialization of array
#a = [a, b, c]
This code will work equivalently to yours:
class Test
def initialize a, b, c
#a = a, b, c
#b = b
#c = c
end
end
Addition:
The order of evaluating is significant.
If you try this expression:
#a = a, (#b = b, #c = c)
Firstly, it will evaluate everything in parentheses:
#b = b, #c = c #=> #b = [b,c] and #c = c
So then we'll get this
#a = [a,[b,c]]
#b = [b,c]
#c = c
Parallel assignment looks like this:
#a, #b, #c = a, b, c
Not just in ruby, it looks like that in every language that does parallel assignment.
#a is being initialized with an array of values. You could write a = 1, 2, 3 and a will be assigned with the list [1, 2, 3]. Hence #a in your example is being assigned as a list of three values 'a1', 'b1' and 'c1'.
To break it up, it is seen as #a = a, #b = b, #c = c.
What you might have wanted is simply:
class Test
def initialize a, b, c
#a = a; #b = b; #c = c
end
end
Comma in assignment statement treated specially in Ruby. Thus above result.
#a = a, #b = b, #c = c
In above expression, Ruby sees first = as part of an assignment statement. Ruby sees values in right hand side of equal (rvalues) as comma separated values, thus, evaluate each of comma separated values first. This cause #b and #c to get assigned, each of these expression return the assigned values also.
Then, since #a is the only item in left hand side of the assignment statement, rvalues are treated as an array, and the array is assigned into #a. Values are consists of a, b and c. This is what happened.
So, if you want three assignments to happen, then, separating assignments by semicolon
to make all of them statements make Ruby happy.
Note that, You can state multiple items in left hand side of the = too.