Single line multiple assignment in ruby - ruby

Is it good to assign an empty array in one line ?
arun#arun:~$ irb
irb(main):001:0> a = b = c = []
=> []
irb(main):002:0> b << "one"
=> ["one"]
irb(main):003:0> p a
["one"]
=> nil
Since i expect 'a' to be [] but it show the value of b means "one". Is this expect one ?
I also try with string and integer object.
irb(main):004:0> d = e = f = 0
=> 0
irb(main):005:0> f = 6
=> 6
irb(main):006:0> p d
0
=> nil
irb(main):007:0>
irb(main):007:0> q = w = e = r = "jak"
=> "jak"
irb(main):008:0> e = "kaj"
=> "kaj"
irb(main):009:0> p w
"jak"
=> nil
irb(main):010:0> p e
"kaj"
=> nil
irb(main):011:0>
It is working as i expected. Then why not array ?

What you are doing is assingning [] to c, which you then assign to b, which finally gets assigned to a.
>> a = b = c = []
>> a.object_id
=> 2152679240
>> b.object_id
=> 2152679240
>> c.object_id
=> 2152679240
What you want is
>> a,b,c = [], [], []
=> [[], [], []]
>> a.object_id
=> 2152762780
>> b.object_id
=> 2152762760
>> c.object_id
=> 2152762740
Edit: the examples work because you just go and plain assign a new value (Fixnums can't be mutated anyway). Try modifying the string in-place instead:
>> q = w = e = r = "jak"
=> "jak"
>> e << 'i'
=> "jaki"
>> w
=> "jaki"

In Ruby everything is an object, including [], 0, and "jak" (an instance of Array, Integer, and String, respectively). You are assigning the same object to multiple variables -- if you change that object (b << "one"), then every variable referencing that object will reflect the change. When you use the assignment operator = you are not changing the object -- you are assigning a new object to the variable.
In Michael Kohl's last example (), he is using << which modifies the String object referenced by the variable. That is why the variable still references the same object and all strings referencing that object reflect the change.

Related

How to change a value in an array via a hash?

I want to change the value of an array via a hash, for example:
arr = ['g','g','e','z']
positions = {1 => arr[0], 2 => arr[1]}
positions[1] = "ee"
Problem is that the one that changed is hash and not array. When I do p arr It still outputs ['g','g','e','z']. Is there a way around this?
You're going to need to add another line of code to do what you want:
arr = ['g','g','e','z']
positions = {1 => arr[0], 2 => arr[1]}
positions[1] = "ee"
arr[0] = positions[1]
Another option would be to make a method that automatically updated the array for you, something like this:
def update_hash_and_array(hash, array, val, index)
# Assume that index is not zero indexed like you have
hash[index] = val
array[index - 1] = val
end
update_hash_and_array(positions, arr, "ee", 1) # Does what you want
This is possible to code into your hash using procs.
arr = ['g','g','e','z']
positions = {1 => -> (val) { arr[0] = val } }
positions[1].('hello')
# arr => ['hello', 'g', 'e', 'z']
You can generalize this a bit if you want to generate a hash that can modify any array.
def remap_arr(arr, idx)
(idx...arr.length+idx).zip(arr.map.with_index{|_,i| -> (val) {arr[i] = val}}).to_h
end
arr = [1,2,3,4,5,6]
positions = remap_arr(arr, 1)
positions[2].('hello')
# arr => [1,'hello',3,4,5,6]
positions[6].('goodbye')
# arr => [1,'hello',3,4,5,'goodbye']
But I'm hoping this is just a thought experiment, there is no reason to change the way array indexing behavior works to start from 1 rather than 0. In such cases, you would normally just want to offset the index you have to match the proper array indexing (starting at zero). If that is not sufficient, it's a sign you need a different data structure.
#!/usr/bin/env ruby
a = %w(q w e)
h = {
1 => a[0]
}
puts a[0].object_id # 70114787518660
puts h[1].object_id # 70114787518660
puts a[0] === h[1] # true
# It is a NEW object of a string. Look at their object_ids.
# That why you can not change value in an array via a hash.
h[1] = 'Z'
puts a[0].object_id # 70114787518660
puts h[1].object_id # 70114574058580
puts a[0] === h[1] # false
h[2] = a
puts a.object_id # 70308472111520
puts h[2].object_id # 70308472111520
puts h[2] === a # true
puts a[0] === h[2][0] # true
# Here we can change value in the array via the hash.
# Why?
# Because 'h[2]' and 'a' are associated with the same object '%w(q w e)'.
# We will change the VALUE without creating a new object.
h[2][0] = 'X'
puts a[0] # X
puts h[2][0] # X
puts a[0] === h[2][0] # true

What's the difference `{'x'=> 3}` and `{x: 3}`?

I have this:
a = {'x' => 3}
b = {'x': 3}
c = {x: 3}
d = {:x => 3}
e = {:'x' => 3}
So, I have that b = c = d = e = {:x => 3}, meanwhile a = {"x" => 3} but a.class == b.class.
I don't understand what the difference is between a and the rest of variables.
In b,c,d, and e, the key is a Symbol.
In a, the key is a String.
a = { 'x' => 3 } #=> { "x" => 3 }
b = { 'x': 3 } #=> { :x => 3 }
c = { x: 3 } #=> { :x => 3 }
d = { :x => 3 } #=> { :x => 3 }
e = { :'x' => 3 } #=> { :x => 3 }
Your variable a hash has "x" key as a string, while other variables have that key as symbol.
Calling class on an object in Ruby returns its class, in your example it is Hash. In other words, the constructor of all hash instances, such as {x: 3} is Hash object.
There is a significant difference between String and Symbol classes in ruby:
explanation on SO;
good article on the topic.
By convention, all but the very first hash notations cast keys to the Symbol instance, while the first one uses the key (String instance in this particular case) as is. (To be more precise: b and c cast key to the Symbol instance, d and e do not cast anything, but keys given in these cases are Symbol instances already.)
Since ('x' == :x) == false, a hash differs from the latters.

Is there a Ruby shorthand for assigning a value from an array only if it is not nil?

I have the following code:
via = reminders_array[i]['value']['bot-input-keyword'] unless reminders_array[i]['value']['via'].nil?
and
collection = json_element['COLLECTION']['$'] unless json_element['COLLECTION'].nil?
When I search for examples, I saw people use ||= but I can't quite understand its use, especially in the second case.
Consider the the following statements:
e #=> NameError: undefined local variable or method `e'...
if false
e = 7
end
e #=> nil
f #=> NameError: undefined local variable or method `f'
f = 7 if false
f #=> nil
As you see, Ruby raises an exception if the value of a local variable (e or f) is sought, but if the variable first appears in a statement, but is not assigned a value, Ruby sets its value to nil. We see that:
a = b unless c.nil?
is the same as:
a = b if c
which is the same as:
a = (c ? b : nil)
which is the same as:
a = c && b
To see why the last expression is equivalent to the preceding two, let's try a couple of examples, both with:
b = 4
c = 2
c && b #=> (2 && 4) => 4
c = nil
c && b #=> (nil && 4) => nil
So I believe you want:
via = reminders_array[i]['value']['via'] &&
reminders_array[i]['value']['bot-input-keyword']
You asked about ||=, which is a form of abbreviated assignment. When Ruby's sees a ||= b she makes the assignment a = b if a is nil or false:
a || a = b
Two more examples, with b still equal to 4:
a = 2
a ||= 4 #=> a || a = 4 => a => 2 || a = 4) => 2
a = nil
a ||= 4 #=> a || a = 4 => a => nil || a = 4 #=> 4
So if a is nil, a remains nil; else a is set to b.
A common use of ||= is:
a = nil
(a ||= []) << 4
#=> [4]
a = [4]
(a ||= []) << 6
#=> [4,6]
In the first instance, a is nil, so a ||= [] converts a to an empty array, to which it appends 4. In the second case, a ([4]) is not nil, so a ||= [] leaves a unchanged ([4]). 6 is then appended to a, making a [4,6].
In addition to ||= there are:
a &&= b #=> a && a = b
a += b #=> a = a + b
a *= b #=> a = a * b
and others (see the doc). A word about a && b. If a is nil, a && b is nil. If a is not nil, a && b is b. Notice that for operators op other than || and &&, a op= b is expanded to a = a op b.

Confusion with yield call to outer block

CODE-I
def sample
x = "hi"
puts " #{x}"
x = yield
puts " #{x}"
end
In the below code block {} from here => sample {"hellooo"} called
yield and assigned "hellooo" to x. Looks good and as expected.
sample{'helloo'}
# >> hi
# >> helloo
CODE-II
o = Object.new
def o.each
x = yield
p x
x = yield
p x
x = yield
p x
end
e = o.to_enum # => #<Enumerator: #<Object:0x007fd1d20494e8>:each>
Why the same not happened in the below call with e.next "sample", as the p didn't printed anything?
e.next {"sample"} # => nil
e.next # => nil
# >> nil
EDIT (Here how enum#feed did the change with the help of yield?)
o = Object.new
=> #<Object:0x2299d88>
def o.each
x = yield
p x
x = yield
p x
x = yield
p x
end
=> nil
e=o.to_enum
=> #<Enumerator: #<Object:0x2299d88>:each>
e.next
=> nil
e.feed "hi"
=> nil
e.next
"hi"
=> nil
next does not take a block. So if you pass it one, it simply ignores it.
It is not possible to simulate something being returned from the block when using the next method of an enumerator. When using an to_enum, the block given to the each method will always return nil except if a value has previously supplied by the feed method.

How to determine named group if regex is input by user

str = "cruel world"
#pattern can be /(?<a>.)(?<b>.)/ OR /(?<b>.)(?<a>.)/
#which was inputted by user, we don't know which one will be picked up by user.
pattern = params[:pattern]
please using str.scan(/#{pattern}/) or other match methods expect output:
p a
# ["c", "u", "l", "w", "r"]
p b
# ["r", "e", " ", "o", "l"]
p c
# [ ]
# there is no named group: `?<c>` in this case. However, we should take it if user inputted.
This is my solution:
str = "cruel world"
#case 1
pattern = /(?<a>.)(?<b>.)/
a = Array.new
b = Array.new
c = Array.new
str.scan(/#{pattern}/) do |x|
a << Regexp.last_match(:a) if $~.names.include? "a"
b << Regexp.last_match(:b) if $~.names.include? "b"
c << Regexp.last_match(:c) if $~.names.include? "c"
end
p a
p b
p c
is there a better way?
Here's my solution
def find_named_matches(str, pattern)
names = pattern.names
return Hash[names.zip [[]] * names.size] unless str =~ pattern
Hash[names.zip str.scan(pattern).transpose]
end
And tests
describe 'find_named_matches' do
example 'no matches' do
find_named_matches('abcabcabc', /(?<a>x.)(?<b>.)/).should == {'a' => [], 'b' => []}
end
example 'simple match' do
find_named_matches('abc', /(?<a>.)/).should == {'a' => %w[a b c]}
end
example 'complex name' do
find_named_matches('abc', /(?<Complex name!>.)/).should == {'Complex name!' => %w[a b c]}
end
example 'two simple variables' do
find_named_matches('cruel world', /(?<a>.)(?<b>.)/).should ==
{'a' => %w[c u l w r], 'b' => %w[r e \ o l]}
end
example 'two simple variables' do
find_named_matches('cruel world', /(?<b>.)(?<a>.)/).should ==
{'b' => %w[c u l w r], 'a' => %w[r e \ o l]}
end
example "three variables and matched chars that aren't captured" do
find_named_matches('afk1bgl2chm3', /(?<a>.)(?<f>.)(?<k>.)./).should ==
{'a' => %w[a b c], 'f' => %w[f g h], 'k' => %w[k l m]}
end
example 'complex regex' do
find_named_matches("the dog's hog is the cat's rat", /(?<nouns>(?:(?<=the |'s ))\w+)/).should ==
{'nouns' => %w[dog hog cat rat]}
end
end

Resources