0

Given the following arrays:

arr1 = [
  { nested_test1: [1,2] },
  { nested_test2: [true, false] }
]
arr2 = [
  { test1: 'a', test2: 'b' }
]

I wanna loop over array arr2 and mutate it depending on the contents of arr1. The final result should look like this:

res_arr = [
  { test1: 'a', test2: 'b', nested_test1: 1, nested_test2: true },
  { test1: 'a', test2: 'b', nested_test1: 2, nested_test2: true },
  { test1: 'a', test2: 'b', nested_test1: 1, nested_test2: false },
  { test1: 'a', test2: 'b', nested_test1: 2, nested_test2: false },
]

So my initial setup looks like this:

res_arr = []
arr1.each do |hash1|
  arr2.each do |hash2|
    hash1.values.first.each do |value|
      hash2[hash1.keys.first] = value
      res_arr << hash2.clone
    end
  end
end

But this gives me the following:

res_arr = [
  {:test1=>"a", :test2=>"b", :nested_test1=>1},
  {:test1=>"a", :test2=>"b", :nested_test1=>2},
  {:test1=>"a", :test2=>"b", :nested_test1=>2, :nested_test2=>true},
  {:test1=>"a", :test2=>"b", :nested_test1=>2, :nested_test2=>false}
]

So what I wanna do then, is to use the mutated version for each iteration of arr2. How can this be achieved?

1
  • “Loop over array arr2” suggests that that array may contain more than one element. One can guess what the desired return value might be In the general case, but it is not made clear by your example. I suggest you add a second element to arr2. Commented Sep 17, 2019 at 15:26

2 Answers 2

1

I'm not entirely clear what your goal here is. For instance, in arr2, can values of the hash be arrays? In arr1 can the hashes have more than one key value pair? Or what happens if arr2 has multiple hashes in it? Are they merged, or is the result expanded further somehow?

So it's hard to say exactly what the best course is.

Nonetheless, given the data you've given us and the result you say you want, you can get it like this using Array#product:

m = arr1.inject(&:merge)
m.values[0].product(m.values[1]).map {|a| Hash[m.keys.zip(a)].merge(arr2[0]) }

The intuition here is that I think you're trying to create all possible combinations of a couple of arrays, to generate all possible test cases? Depending on what you're trying to do, a more natural way to store the data and get the result might be:

m = { 
  test1: ['a'], 
  test2: ['b'],
  nested_test1: [1, 2],
  nested_test2: [true, false]
}

m.values.inject(&:product).map {|v| Hash[m.keys.zip(v.flatten)] }
Sign up to request clarification or add additional context in comments.

3 Comments

Works as I intended. Thank you so much!
This only works if arr2 contains only one element. The OP wishes to "loop over arr2", suggesting that it may contain more than one element, despite the example. Moreover, if it were only contain one element there would be no point in making it an array.
@CarySwoveland yes, I agree. I just don't think we know without more information what they want to happen if the array contains multiple elements, so I provided the code for the example given. Your answer is a good guess, but not necessarily what they want (although given that they've accepted it, it probably was what they want)
0

Code

The following method permits its arguments to be arrays of arbitrary size.

def expand(arr1, arr2)
    arr2.product(
      arr1.map { |h| h.flatten.then { |k,v| v.map { |e| { k=>e } } } }.
           then { |first, *rest| first.product(*rest) }
    ).map { |first, (*rest)| first.merge(*rest.flatten) }
end

Example

arr1 = [{ nested_test1: [1,2] }, { nested_test2: [true, false] }]
arr2 = [{ test1: 'a', test2: 'b' }, { test3: 'c', test4: 'd' }]

expand(arr1, arr2)
  #=> [{:test1=>"a", :test2=>"b", :nested_test1=>1, :nested_test2=>true},
  #    {:test1=>"a", :test2=>"b", :nested_test1=>1, :nested_test2=>false},
  #    {:test1=>"a", :test2=>"b", :nested_test1=>2, :nested_test2=>true},
  #    {:test1=>"a", :test2=>"b", :nested_test1=>2, :nested_test2=>false},
  #    {:test3=>"c", :test4=>"d", :nested_test1=>1, :nested_test2=>true},
  #    {:test3=>"c", :test4=>"d", :nested_test1=>1, :nested_test2=>false},
  #    {:test3=>"c", :test4=>"d", :nested_test1=>2, :nested_test2=>true},
  #    {:test3=>"c", :test4=>"d", :nested_test1=>2, :nested_test2=>false}] 

Explanation

The following steps are performed for the example.

Noting that:

arr1.map(&:flatten)
  #=> [[:nested_test1, [1, 2]], [:nested_test2, [true, false]]] 

the first step is the following:

a = arr1.map { |h| h.flatten.then { |k,v| v.map { |e| { k=>e } } }
  #=> [[{:nested_test1=>1}, {:nested_test1=>2}],
  #    [{:nested_test2=>true}, {:nested_test2=>false}]]

See Hash#flatten and Object#then. The latter method made its debut in Ruby v2.6. It is an alias of Object#yield_self, which was new in v2.5.

Then:

b = a.then { |first, *rest| first.product(*rest) }
  #=> [[{:nested_test1=>1}, {:nested_test2=>true}],
  #    [{:nested_test1=>1}, {:nested_test2=>false}],
  #    [{:nested_test1=>2}, {:nested_test2=>true}],
  #    [{:nested_test1=>2}, {:nested_test2=>false}]]

See Array#product. Here and especially below I made heavy use of Array decomposition. See also this article.

Continuing,

c = arr2.product(b)
  #=> [[{:test1=>"a", :test2=>"b"}, [{:nested_test1=>1}, {:nested_test2=>true}]],
  #    [{:test1=>"a", :test2=>"b"}, [{:nested_test1=>1}, {:nested_test2=>false}]],
  #    [{:test1=>"a", :test2=>"b"}, [{:nested_test1=>2}, {:nested_test2=>true}]],
  #    [{:test1=>"a", :test2=>"b"}, [{:nested_test1=>2}, {:nested_test2=>false}]],
  #    [{:test3=>"c", :test4=>"d"}, [{:nested_test1=>1}, {:nested_test2=>true}]],
  #    [{:test3=>"c", :test4=>"d"}, [{:nested_test1=>1}, {:nested_test2=>false}]],
  #    [{:test3=>"c", :test4=>"d"}, [{:nested_test1=>2}, {:nested_test2=>true}]],
  #    [{:test3=>"c", :test4=>"d"}, [{:nested_test1=>2}, {:nested_test2=>false}]]]

and lastly:

c.map { |first, (*rest)| first.merge(*rest.flatten) }
  #=> <as shown above>

See Hash#merge.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.