What is the fastest way of combining elements of lists in rules?

Let us take the following two toy lists:

{{a,b},{c,d}} and {{e,f},{g,h}}

And let's say that I want to combine them such that I get the following:

{{a -> e, b -> f}, {a -> g, b -> h}, {c -> e, d -> f}, {c -> g, d -> h}}

This can obviously be done with Table, MapThread and who knows how many ways. The question is, what is the fastest way in terms of computational time?

EDIT

The lists need not to be of the same dimensions. Fore example, one could have:

{a,b} and {{c,d},{e,f}}

to get {{a -> c, b -> d}, {a -> e, b -> f}}.

Only the last levels of the lists need to have the same number of elements in order to combine them into Rules.

• I'll test the answers on my actual dataset and get back to you to see what is the response of AbsoluteTiming for each of your solutions. Best performance gets accepted answer, obivously. Stay tuned :D Commented Dec 8, 2017 at 13:48
• Turns out the fastest one is using AssociationThread, at least in my code (which has way longer lists). Commented Dec 12, 2017 at 14:52

One straightforward way is with AssociationThread.

keys = {{a, b}, {c, d}}
values = {{e, f}, {g, h}}

(* {<|a -> e, b -> f|>, <|a -> g, b -> h|>,
<|c -> e, d -> f|>, <|c -> g, d -> h|>} *)


And for the second example:

keys = {{a, b}}
values = {{e, f}, {g, h}}

(* {<|a -> d|>, <|a -> f|>, <|b -> d|>, <|b -> f|>} *)


Of course, you end up with a list of Associations, but is that such a bad thing? You can always turn it into a list with Normal.

As for the second example, it clearly doesn't meet your "lists need not be of the same dimensions" specification. You can either take the one liner and use it on keys = {{a, b}} instead of {a, b}, or you can use this longer function, but get the functionality you're after:

listtorules[list1_, values_] :=
Block[{keys = If[Depth[list1] == 2, {list1}, list1]},
]


Then

listtorules[{a, b}, {{c, d}, {e, f}}]

listtorules[{{a, b}}, {{c, d}, {e, f}}]


both produce

{{a -> c, b -> d}, {a -> e, b -> f}}

• If you use the rules only for Replace and friends then Normal is actually contraproductive: Associations tend to be even more efficient in replacements than hash tables generated with Dispatch. Commented Dec 7, 2017 at 11:04
• @HenrikSchumacher At least in the examples the set of rules have length 2, and for so short replacement rules Dispatch and Association usually makes things slower Commented Dec 7, 2017 at 11:21
• @HenrikSchumacher and @Coolwater I have a mild preference for Associations over Rules, just because it's often cleaner and can be faster, depending on what you're using them for. I personally wouldn't Normalize the Association, but I wanted to leave both possibilities open for the OP. Commented Dec 7, 2017 at 11:31
• @Coolwater Good point. I tend to project everything I see to larger list... However, the OP's question was also about performance. Commented Dec 7, 2017 at 11:49
• @aardvark2012 That was not meant as criticism. I stumbled upon this fact quite unexpectedly (note that Dispatch is actually made for Replace) some time ago. I just thought this could also to be of interest to the OP. Commented Dec 7, 2017 at 11:52
L1 = {{a, b}, {c, d}};
L2 = {{e, f}, {g, h}};

L1r = ArrayReshape[L1, {Times @@ Most[#], Last[#]}] &[Dimensions[L1]];
L2r = ArrayReshape[L2, {Times @@ Most[#], Last[#]}] &[Dimensions[L2]];

new = Transpose[Tuples[{L1r, L2r}], {1, 3, 2}];
new[[All, All, 0]] = Rule;

new

{{a -> e, b -> f}, {a -> g, b -> h}, {c -> e, d -> f}, {c -> g, d -> h}}


Use Outer:

lst01 = {{a, b}, {c, d}}
lst02 = {{e, f}, {g, h}}

You'll have to use {{a,b}} instead of {a,b} for your second case. And if you really need not have a matrix of results, you can Catenate the results of the above (but of course that has a cost).