In Ruby, with a 1D Array
, I can dynamically select elements by passing an Integer
key in brackets, as follows:
example = [0,1,2,[3,4]]
i = 2
example[i]
==> 2
What I would like to achieve, is to dynamically update an element in a multi-dimensional Array
by passing an Array
of Integers
, each representing the index to select in each array. Example of what I'd like to achieve:
example = [0,1,2,[3,4]]
path = [3, 1] (corresponds to the 4)
example[*path or some other syntax] = 9
example
==> [0,1,2,[3,9]]
What I've tried is storing the result with each path iteration:
temp = example
path.each {|index|
temp = temp[index]
}
temp
==> 4
This successfully identifies the element I'd like to update. However, it appears to have stored a copy, rather than to reference the original location, as:
temp = 9
example
==> [0,1,2,[3,4]]
How can I update the base array example
without hardcoding path
in individual brackets?
Clarification after a comment: I don't know the path
length in advance, which is why hardcoding isn't viable.
CodePudding user response:
Define a function, something along the lines of:
def deep_set! val, arr, *path
path[0...-1].each {|i| arr = arr[i]}
arr[path[-1]] = val
end
arr = [0,1,2,[3,4]]
deep_set! 9, arr, 3 ,1
arr #=> [0, 1, 2, [3, 9]]
CodePudding user response:
Your iteration is almost there, you just need to stop one step before you run through path
so that you can have the array you need to modify rather than the element.
So split the path
into the pieces you want:
*p, target = path
# [3], 1
Then use #inject
to find the array:
ary = p.inject(example) { |i, a| a[i] }
# [3, 4]
and then do your assignment:
ary[target] = 9
Of course you'll need to add some logic to deal with the unexpected such as path
leading you to a non-array element or path
not matching the structure of example
(consider path = [11, 6, 23]
in your example).