Iterator calls all elements at once within an each block - ruby

I build the array here:
def initialize
#names = []
end
#names << page.all('//*[#id="USERS_AVAIL"]/option').map {|result| result.text.split(", ")}
later on I'm trying to compile and visit url's by iterating through the names array like so:
#names.each do |name|
visit "https://example.com/k=#{name}&tpe=1"
end
Some puts statements show me that the each method is calling every element of the array all at once instead of iterating as intended. I.E.: "https://example.com/k=#{[[%22Adviento%22,%20%22Justin%22],%20[%22Asamoah%22,%20%22Nathan%22],%20[%22Baughman%22,%20%22Zachary%22],}&tpe=1". #names.length has a count of only 4 but a puts of the #names array shows the proper output? I'm not sure what could be wrong, thanks in advance for any assist.

Replace << with +=. The << is inserting the entire array as a single element of its own, whereas += will concatenate the array, which seems to be your intention.
For example:
a = [1,2,3]
# => [1, 2, 3]
a << [4,5,6]
# => [1, 2, 3, [4, 5, 6]] # WRONG
a = [1,2,3]
# => [1, 2, 3]
a += [4,5,6]
# => [1, 2, 3, 4, 5, 6] # CORRECT
Try:
#names += page.all('//*[#id="USERS_AVAIL"]/option')
.map { |r| r.text.split(',').map(&:strip) }.flatten
If the quotes are in the literal form %22 and you want to capture the strings in between them:
#names += page.all('//*[#id="USERS_AVAIL"]/option')
.map { |r| r.text.scan(/%22([^%]+)%22/) }.flatten

Related

How to refactor this code to remove output variable?

def peel array
output = []
while ! array.empty? do
output << array.shift
mutate! array
end
output.flatten
end
I have not included the mutate! method, because I am only interested in removing the output variable. The mutate! call is important because we cannot iterate over the array using each because array is changing.
EDIT: I am getting an array as output, which is what I want. The method works correctly, but I think there is a way to collect the array.shift values without using a temp variable.
EDIT #2: OK, here is the mutate! method and test case:
def mutate! array
array.reverse!
end
a = (1..5).to_a
peel( a ).should == [ 1, 5, 2, 4, 3 ]
It doesn't matter if peel modifies array. I guess it should be called peel!. Yes, mutate! must be called after each element is removed.
All this reversing makes me dizzy.
def peel(array)
indices = array.size.times.map do |i|
i = -i if i.odd?
i = i/2
end
array.values_at(*indices) # indices will be [0, -1, 1, -2, 2] in the example
end
a = (1..5).to_a
p peel(a) #=>[1, 5, 2, 4, 3]
Another approach:
def peel(array)
mid = array.size/2
array[0..mid]
.zip(array[mid..-1].reverse)
.flatten(1)
.take(array.size)
end
Usage:
peel [1,2,3,4,5,6]
#=> [1, 6, 2, 5, 3, 4]
peel [1,2,3,4,5]
#=> [1, 5, 2, 4, 3]
Here's a way using parallel assignment:
def peel array
n = array.size
n.times {|i| (n-2-2*i).times {|j| array[n-1-j], array[n-2-j] = array[n-2-j], array[n-1-j]}}
array
end
peel [1,2,3,4,5] # => [1,5,2,4,3]
peel [1,2,3,4,5,6] # => [1,6,2,5,3,4]
What I'm doing here is a series of pairwise exchanges. By way of example, for [1,2,3,4,5,6], the first 6-2=4 steps (6 being the size of the array) alter the array as follows:
[1,2,3,4,6,5]
[1,2,3,6,4,5]
[1,2,6,3,4,5]
[1,6,2,3,4,5]
The 1, 6 and the 2 are in now the right positions. We repeat these steps, but this time only 6-4=2 times, to move the 5 and 3 into the correct positions:
[1,6,2,3,5,4]
[1,6,2,5,3,4]
The 4 is pushed to the end, it's correct position, so we are finished.

What is the difference between << and +=?

I have been playing around with arrays a bit, and found myself in trouble understanding the following code:
first_array = []
second_array = []
third_array = [] # I initialized 3 empty arrays
third_array << [1,2,3,4,5,6,7,8,9] # I loaded 1..9 into third_array[0]
puts third_array.size # => 1
first_array << third_array # 1..9 was loaded into first_array[0]
second_array += third_array # 1..9 was loaded into second_array[0]
puts first_array == third_array # false
puts second_array == third_array # true
puts first_array == second_array # false
puts first_array.size # 1
puts second_array.size # 1
puts third_array.size # 1
What happened with this?
second_array += third_array # I have no clue
Why aren't all the arrays equal to each other?
They exhibit fairly different behaviors. One creates and assigns a new Array object, the other modifies an existing object.
+= would be the same as second_array = second_array + third_array. This sends the + message to the second_array object passing third_array as the argument.
Per the documentation Array.+ returns a new array object built by concatenating the two arrays. This will return a new object.
Array.<< simply push the parameter to the end of the existing array object:
second_array = []
second_array.object_id = 1234
second_array += [1,2,3,4]
second_array.object_id = 5678
second_array << 5
second_array.object_id = 5678
There is also a difference in how the parameter is added. By adding other elements, it will help see why your arrays are not equal:
second_array = [1, 2, 3]
# This will push the entire object, in this case an array
second_array << [1,2]
# => [1, 2, 3, [1,2]]
# Specifically appends the individual elements,
# not the entire array object
second_array + [4, 5]
# => [1, 2, 3, [1,2], 4, 5]
This is because Array.+ uses concatenation instead of pushing. Unlike Array.concat which modifies the existing object, Array.+ returns a new object.
You can think of a Ruby implementation like:
class Array
def +(other_arr)
dup.concat(other_arr)
end
end
In your specific example, your objects look like this at the end:
first_array = [[[1, 2, 3, 4, 5, 6, 7, 8, 9]]] # [] << [] << (1..9).to_a
second_array = [[1, 2, 3, 4, 5, 6, 7, 8, 9]] # [] + ([] << (1..9).to_a)
third_array = [[1, 2, 3, 4, 5, 6, 7, 8, 9]] # [] << (1..9).to_a
<< appends an item to an array
+= adds an array to an array.
Examples:
[1,2] << 3 # returns [1,2,3]
[1,2] += [3,4] # returns [1,2,3,4]
The last difference not mentioned so far between << and += is, that << is a method:
class Array
def << other
# performs self.push( other )
end
end
whereas += is syntax:
a += b
and is merely a shorthand for writing:
a = a + b
So, in order to modify += behavior, one has to modify the + method:
class Array
def + other
# ....
end
end

Did the `each` method change its behavior?

A Ruby exercise about multidimensional array said that two instances of each method are necessary to access the inner elements of a multidimensional array. The following:
x = [[1,2],[3,4],[5,6]]
x.each do |a|
a.each do |b|
puts b
end
end
should return:
# 1
# 2
# 3
# 4
# 5
# 6
However, it's not necessary to use two each methods. If I just do
x.each { |a| puts a }
I get the same result. It seems a single instance of each already goes to the inner level of multidimensional arrays.
In that case, how would I access the first level? In other words, how would I get the following?
# [1,2]
# [3,4]
# [5,6]
There are three different print functions in Ruby. Let's try them in the Ruby prompt:
> puts [1,2]
1
2
=> nil
> p [1,2]
[1, 2]
=> [1, 2]
> print [1,2]
[1, 2]=> nil
In case you aren't familiar with irb, the expression following the fat arrow => is the return value of the statement.
Moreover, if you do just
puts x
you'll get exactly the same result. This is because puts treats arrays in a special manner. It enumerates all elements and calls puts on them individually. (this is recursive, as you might imagine).
This will get roughly the output you want:
x.each {|a| p a}
or
x.each {|a| puts a.inspect }
Output
# >> [1, 2]
# >> [3, 4]
# >> [5, 6]
x.each { |a| puts a }
This will call puts on each element of the x array.
It is the same as doing :
puts [1,2]
puts [3,4]
puts [5,6]
puts on an array will format it like you saw.

Why are the values empty in a Hash after inserting an array?

I have an array of numbers where I want the individual numbers to be the key and the array itself to be the value. Doing this poses no problems
keys.each do |i|
myHash[i] = keys
end
But now I want the values to be the array minus the first value for every subsequent iteration so I did this
keys = Array.new
numbers.each do |i|
keys.push(i)
end
keys.each do |i|
# puts i
# puts numbers.inspect
myHash[i] = numbers
numbers.shift
end
And it gives me empty arrays as the values in my hash for all the keys. Why is that? Ultimately, I want my hash to look like this given an array of [1, 2, 3, 4]
{1=>[1, 2, 3, 4], 2=>[2, 3, 4], 3=>[3, 4], 4=>[4]}
Thank you!
You are not doing a deep copy of the array.
Try :
keys.each do |i|
# puts i
# puts numbers.inspect
myHash[i] = numbers.clone
numbers.shift
end

add element to ruby array return new array

I would like to add an element to an array but without actually changing that array and instead it returning a new one. In other words, I want to avoid:
arr = [1,2]
arr << 3
Which would return:
[1,2,3]
Changing arr itself. How can I avoid this and create a new array?
You can easily add two arrays in Ruby with plus operator. So, just make an array out of your element.
arr = [1, 2]
puts arr + [3]
# => [1, 2, 3]
puts arr
# => [1, 2]
it also works by extending arr using * operator
arr = [1,2]
puts [*arr, 3]
=> [1, 2, 3]

Resources