assigning values of array into variables in ruby - arrays

I am trying to assign the each value of an array into different variables. But I am getting error as "Expecting end."
strs = ["flower","flow","flight"]
strs.each_with_index do |x, i|
"b#{i}" = x
end
what is going wrong here ?

What you're doing is like saying
"howdy" = 1
You can't say that. That expression attempts to assign into a string literal. You can't do that. A string literal is not an lvalue (a thing that can go on the left side of an equal sign).
If you are trying to say "make a variable called howdy and assign this value to it", you can't do that because local variables cannot be created on the fly. See How to dynamically create a local variable?.
However, the real core of your issue is that you should not even want to do what you're doing. You already have an array, a wonderful thing that allows you to reference each entry by number. "flower" in your code is already strs[0], and so on. The whole point of the array is that it lets you do that. There is thus no need for individual variables with number names; the array is the variable with number names.

I'm not sure as to WHY you are attempting to create local variables when you already sort of have them. Consider the following where we simply replace your suggested b0 with b[0]:
strs = ["flower","flow","flight"]
b = strs
b[0] #=> "flower"
You could optionally convert your array to a hash using an approach similar to what you've already tried and replace b0 with b[0]:
b = {}
strs.each_with_index do |x, i|
b[i] = x
end
b[0] #=> "flower"
Or even something like this:
my_variables = {}
strs.each_with_index do |x, i|
my_variables[:"b#{i}"] = x
end
my_variables[:b0] #=> "flower"

Related

Can't shovel string into new array in Ruby

I am trying to search an array for a substring and if that substring exists, shovel it into a new array. The problem I am having is that it keeps coming back with this error message:
`block in substrings': undefined method `<<' for nil:NilClass
I have verified that the index in the method is not nil by printing it. I have also done index == nil to double check.
What am I missing here?
Thanks in advance for your help!
new_array = []
def substrings(word, array)
new_array = array.each do |index|
if index.include? (word)
p index
p index == nil
new_array << index
end
end
end
dictionary = ["below", "down", "go", "going", "horn", "how", "howdy", "it", "i", "low", "own", "part", "partner", "sit"]
substrings("i", dictionary)
You basically combine two different ways of solving this problem. The first is to assign the new_array the result of looping though the array, but in that case, the new_array variable is not available to use inside the block.
So you could either choose to create the variable first, like this
new_array = []
array.each do |index|
if index.include?(word)
new_array << index
end
end
Alternatively you could use a method called reduce which takes a more functional programming approach. That could look like this
new_array = array.reduce([]) do |arr, index|
if index.include?(word)
arr << index
else
arr
end
end
What reduce does is that the block argument arr is always set to the return value of the previous block execution. that can make the syntax a little longer than it has to be, so Ruby also has an alternate approach to reduce, called each_with_object, that does the same, but by mutating the same variable, instead of requiring a return value. I actually prefer this way and would solve it like this.
new_array = array.each_with_object([]) do |index, arr|
arr << index if index.include?(word)
end
I would like to extend a bit the (correct) answer given by #DanneManne: While it is correct that you can access local variables from outer blocks from within an inner block, you can't do it within a def, and it is unnecessary in your example, because you can initialize new_array inside the body of your method and return it as result. But in case you ever really need this kind of construct, there indeed is a workaround:
So, this does NOT work:
a=5
def f
puts a; # WRONG. a is not known here
end
and this works different than you seem to expect:
a=5
def f
a=6
end
puts a # prints 5
f
puts a # prints 5 again
But if you define your method like this, it works:
a=5
define_method(:f) do
puts a; # OK, refers to outer variable a
end
By using a block, you create a closure with this, so if you do now a
f;
a=6;
f
5 and 6 is printed, in this order.
I have verified that the index in the method is not nil by printing it. I have also done index == nil to double check.
What am I missing here?
Not index is nil, but new_array. The error message says:
undefined method `<<' for nil:NilClass
and refers to the line new_array << index. Here, << is the method and new_array is the receiver. And for some reason, new_array is nil.
You probably expected new_array to be [] because you explicitly said new_array = []. But methods have their own local variable scope. If you define a local variable outside of a method, it won't be available inside, or vice-versa.
Typically when referring to an undefined variable you'd get:
undefined local variable or method `new_array'
but here, the 2nd assignment conceals the actual problem:
new_array = array.each do |index|
^^^^^^^^^^^
When Ruby encounters this line, it immediately creates a local variable new_array with an initial value of nil. (local variables are created when the line is parsed, not when the assignment occurs)
To get the expected result, you have to move new_array = [] into the method, get rid of the new_array = array.each { ...} assignment and return new_array at the end:
def substrings(word, array)
new_array = []
array.each do |index|
if index.include?(word)
new_array << index
end
end
new_array
end
The variable names are a bit arbitrary, maybe even misleading. Having index.include?(word) looks like you're comparing a numerical index to a string. I'd use something like this:
def substrings(substring, words)
result = []
words.each do |word|
if word.include?(substring)
result << word
end
end
result
end
Code-wise, you can incorporate the array into the loop via each_with_object which will also return the array:
def substrings(substring, words)
words.each_with_object([]) do |word, result|
if word.include?(substring)
result << word
end
end
end
However, selecting elements based on a condition is such a common task that Ruby provides a dedicated method select – you merely have to return true or false from the block to indicate whether the element should be selected:
def substrings(substring, words)
words.select do |word|
word.include?(substring)
end
end

Why is one of these functions changing the original array and not the other?

I'm not sure what I am missing by looking at these three functions.
The desired result for these functions would be that the original array changes value so
change_this = [6, 7, 8]
array_times_two!(change_this)
change_this == [12, 14, 16] => true
The following function does this
def array_times_two!(array)
array.map! {|x| x * 2}
end
And so does this one...
def array_times_two!(array)
array.each_with_index do |element, index|
array[index] = array[index] * 2
end
array
end
How come the following function does not change the value like the others?
def array_times_two!(array)
array = array.map {|x| x * 2}
array
end
How is the second function changing the array but the third isn't?
In the last example you have array coming in as a local variable. Reassigning the local variable has zero effect on the original variable. This is because Ruby is, in effect, pass by value, except the confusing part is those values are often object references, or a fancy term for pointers.
The consequences of this aren't that complicated though: Calling a method on an object can modify the contents of that object. Reassigning a variable cannot.
The first version of your method is probably the best, but you could make it more generic, like:
def multiply_by!(array, n = 2)
array.map! { |v| v * n }
end
You can also "fix" the last version by using a method call to apply the changes:
def array_times_two!(array)
array.replace(array.map {|x| x * 2})
end
This calls Array#replace to stomp the contents of the original object and force the changes to stick. It's not a very elegant solution when things like map! exist.

Why is it not possible to fill an array using an each do loop in Ruby?

If I use an each do loop to fill an array, it will leave the array as it is (in this case it will be a nil array of size 4)
array = Array.new(4)
array.each do |i|
i = 5
end
I understand that I can initialize an array with my desired value using array = Array.new(4) {desired value} but there are situations in which I'm choosing between different values and I am trying to understand how the each do loop work exactly.
The current way I'm doing it is the following which fills in the array with my desired value
array = Array.new(4)
array.each_with_index do |val, i|
array[i] = 5
end
Solution
You need :
array = Array.new(4) do |i|
5
# or some logic depending on i (the index between 0 and 3)
end
Your code
array = Array.new(4)
array is now an Array with 4 elements (nil each time).
array.each iterates over those 4 elements (still nil), and sets i as block-local variable equal to nil.
Inside this block, you override i with 5, but you don't do anything with it. During the next iteration, i is set back to nil, and to 5, and so on...
You don't change the original array, you only change local variables that have been set equal to the array elements.
The difference is that
i = 5
is an assignment. It assigns the value 5 to the variable i.
In Ruby, assignments only affect the local scope, they don't change the variable in the caller's scope:
def change(i)
i = 5 # <- doesn't work as you might expect
end
x = nil
change(x)
x #=> nil
It is therefore impossible to replace an array element with another object by assigning to a variable.
On the other hand,
array[i] = 5
is not an assignment, but a disguised method invocation. It's equivalent to:
array.[]=(i, 5)
or
array.public_send(:[]=, i, 5)
It asks the array to set the element at index i to 5.

Get variable whose value is the maximum in an array

If I have an array of variables in Ruby:
a = 4
b = 7
c = 1
array = [a, b, c]
How can I get access to the name of the variable that has the highest value? (In this example b) I want to retrieve a reference to the element with the highest value, to be able to later manipulate it:
b += 10
I tried array.max but that just returns the max value 7
When you build an array by writing array = [a, b, c], the spots in the array do not retain any kind of association with the variables named a, b, and c, so you there is no way to do exactly what you are talking about. Those variables can change without affecting the array.
You could change the way you are storing your data in order to make this possible, but without knowing what data you are storing it is hard to recommend a good way to do it. You could consider storing your data inside a hash instead of in loose variables and an array:
hash = { a: 4, b: 7, c: 1}
Ruby does not support passing objects by reference, which is essentially what you are trying to do. When you call array.max, you are passed a copy of the Fixnum 7, and assignment and modification will be applied to this copy.
That is, you can not store a reference to this Array element, and modify it later. You can modify the maximum value on the spot, however, using:
array[array.index(array.max)] = array.max + 10
#=> 17
array
#=> [4, 17, 1]
Note that when there are duplicate values in the Array, array.index(array.max) will return the index of the first one.
Storing the index for later use is not a solid solution, since, while Arrays in Ruby are ordered, the Array or its elements can be modified between the point you retrieve the index, and the point where you decide to change the corresponding value.
Another answer suggests there's a hack to mimic pass-by-reference behaviour, but in doing this, you're working against the intention of the language, and it could lead to a lot of pain down the line.
If you are trying to manipulate the maximum of three values, then, you are better off doing something like this:
array.max + 10 # or any other manipulation for that matter.
If you are asking what you are asking for academic purposes, then, you could use Ruby's Binding to inspect values of local variables.
a = 4
b = 7
c = 1
array = [a, b, c]
# Select variable whose value matches with maximum of three elements
# Variable's name as symbol is obtained by this process
var = binding.send(:local_variables).select do |i|
array.max.eql? binding.local_variable_get(i)
end.first
#=> :b
# Update value of variable using symbol representing its name
binding.local_variable_set(var, binding.local_variable_get(var) + 10)
puts b
#=> 17
You could also use Binding#eval to manipulate the variable, for e.g.:
binding.eval("#{var.to_s} += 10")
#=> 17
Note that we need to use var.to_s as var contains a symbol, and we need to convert it to string to get variable name string that can be used in eval.
You can use Array#index for this..
1.9.3-p484 :008 > max_elem = array.index(array.max)
=> 1
1.9.3-p484 :009 > array[max_elem] = array[max_elem] + 10
=> 11
1.9.3-p484 :010 > array
=> [4, 17, 1]
1.9.3-p484 :011 >

Modify hashes in an array based on another array

I have two arrays like this:
a = [{'one'=>1, 'two'=>2},{'uno'=>1, 'dos'=>2}]
b = ['english', 'spanish']
I need to add a key-value pair to each hash in a to get this:
a = [{'one'=>1, 'two'=>2, 'language'=>'english'},{'uno'=>1, 'dos'=>2, 'language'=>'spanish'}]
I attempted this:
(0..a.length).each {|c| a[c]['language']=b[c]}
and it does not work. With this:
a[1]['language']=b[1]
(0..a.length).each {|c| puts c}
an error is shown:
NoMethodError (undefined method '[]=' for nil:NilClass)
How can I fix this?
a.zip(b){|h, v| h["language"] = v}
a # => [
# {"one"=>1, "two"=>2, "language"=>"english"},
# {"uno"=>1, "dos"=>2, "language"=>"spanish"}
# ]
When the each iterator over your Range reaches the last element (i.e. a.length), you will attempt to access a nonexisting element of a.
In your example, a.length is 2, so on the last iteration of your each, you will attempt to access a[2], which doesn't exist. (a only contains 2 elements wich indices 0 and 1.) a[2] evaluates to nil, so you will now attempt to call nil['language']=b[2], which is syntactic sugar for nil.[]=('language', b[2]), and since nil doesn't have a []= method, you get a NoMethodError.
The immediate fix is to not iterate off the end of a, by using an exclusive Range:
(0...a.length).each {|c| a[c]['language'] = b[c] }
By the way, the code you posted:
(0..a.length).each {|c| puts c }
should clearly have shown you that you iterate till 2 instead of 1.
That's only the immediate fix, however. The real fix is to simply never iterate over a datastructure manually. That's what iterators are for.
Something like this, where Ruby will keep track of the index for you:
a.each_with_index do |hsh, i| hsh['language'] = b[i] end
Or, without fiddling with indices at all:
a.zip(b.zip(['language'].cycle).map(&:reverse).map(&Array.method(:[])).map(&:to_h)).map {|x, y| x.merge!(y) }
[Note: this last one doesn't mutate the original Arrays and Hashes unlike the other ones.]
The problem you're having is that your (0..a.length) is inclusive. a.length = 2 so you want to modify it to be 0...a.length which is exclusive.
On a side note, you could use Array#each_with_index like this so you don't have to worry about the length and so on.
a.each_with_index do |hash, index|
hash['language'] = b[index]
end
Here is another method you could use
b.each_with_index.with_object(a) do |(lang,i),obj|
obj[i]["language"] = lang
obj
end
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
What this does is creates an Enumerator for b with [element,index] then it calls with_object using a as the object. It then iterates over the Enumerator passing in each language and its index along with the a object. It then uses the index from b to find the proper index in a and adds a language key to the hash that is equal to the language.
Please know this is a destructive method where the objects in a will mutate during the process. You could make it non destructive using with_object(a.map(&:dup)) this will dup the hashes in a and the originals will remain untouched.
All that being said I think YAML would be better suited for a task like this but I am not sure what your constraints are. As an example:
yml = <<YML
-
one: 1
two: 2
language: "english"
-
uno: 1
dos: 2
language: "spanish"
YML
require 'yaml'
YAML.load(yml)
#=>[
{"one"=>1, "two"=>2, "language"=>"english"},
{"uno"=>1, "dos"=>2, "language"=>"spanish"}
]
Although using YAML I would change the structure for numbers to be more like language => Array of numbers by index e.g. {"english" => ["zero","one","two"]}. That way you can can access them like ["english"][0] #=> "zero"

Resources