I want to define a method which can take an optional amount of arguments and hashes, like so
def foo(*b, **c)
2.times.map.with_index { |i|
new_hash, new_array = {}, b
c.map { |key, value| new_hash[key] = value[i] unless value[i].nil? }
new_array << new_hash if new_hash.length > 0
send(:bar, new_array)
}
end
def bar(*b)
p b
end
If I've understood the splat and double splat operators correctly (which I doubt), then this should send the array b
to the bar
method, and only adding the new_hash
from foo
if it contains something. However, something weird happens - I'll try and illustrate with some snippets below
# invoking #foo
foo(a, key: 'value')
# first iteration of loop in #foo
# i is 0
# b is []
# c is { :key => ['value1'] }
# send(:bar, new_array) => send(:bar, [{:key => 'value1'}])
# bar yields: [{:key => 'value1'}]
Now, however, something happens
# second iteration of loop in #foo
# i is 1
# b is [:key => 'value1'] <---- why?
# c is { :key => ['value1']
Why has the value of b
changed inside the loop of foo
?
edit Updated the code to reflect a new array is created for each iteration
new_hash, new_array = {}, b
This doesn't create a copy of b
. Now new_array
and b
point to the same object. Modifying one in-place will modify the other.
new_array << new_hash
That modifies new_array
(and thus b
) in place, so the new element remains on the next iteration. Use something like +
, which creates a copy:
send(:bar, *(b + (new_hash.empty? ? [] : [new_hash])))