# Using MapThread on list of lists

Just a light weight question for learning. If you think this was asked before, please feel free to close it.

I am trying to learn MapThread more, and wanted to use it to generate Rule from a list like this

lst = {{1, 2},{a, b}}


in order to obtain

(* {1 -> a, 2 -> b}  *)


Using MapThread works nice:

MapThread[Rule, lst]
(* {1 -> a, 2 -> b}  *)


But when the list contain more than one list in it, like this

lst = {{{1, 2}, {a, b}}, {{3, 4}, {c, d}}}


Then I could not find a way to use MapThread on its own on the whole list. It needs help to iterate over all the lists. So what I do now is Map the function MapThread on each list in the list. Like this

Map[ MapThread[Rule, #] &,  lst]
(*  {{1 -> a, 2 -> b}, {3 -> c, 4 -> d}}  *)


I did not want to do this:

MapThread[Rule, ArrayFlatten[{lst}]]
(* {1 -> a, 2 -> b, 3 -> c, 4 -> d} *)


because it loses the structure I had at the start, even though I can correct things with

Partition[%, 2]
(* {{1 -> a, 2 -> b}, {3 -> c, 4 -> d}} *)


But it seems not a good way to do it.

Question is: What is the recommended way to do this type of operation? Can MapThread be told to do this on its own without the help of Map? Are there other commands to do the above that you think are better?

I used Rule above, but you can replace that with any function f that takes 2 arguments.

Update 12/26/12

Fyi, I made a small addition to the solution so that it can be also used for 2D matrix, in addition to the 3D matrix (since this was not part of what I was asking originally).

Here is my changes. I found that the trick is to tell Apply the level to use. For 2D matrix, use level 0 or {}. But for 3D matrix, use {1}. This assumes the input is either 2D or 3D.

So this is how it looks now:

f[lst_List] :=Apply[Thread[Rule[#1, #2]] &, lst,If[MatrixQ[lst], Sequence @@ {}, {1}]]
f[ {{1, 2}, {a, b}} ]
(* {1 -> a, 2 -> b} *)


and

f[ {{{1, 2}, {a, b}}, {{3, 4}, {c, d}}}]
(*  {{1 -> a, 2 -> b}, {3 -> c, 4 -> d}} *)


Another way I could have done this, is by making 2 function definitions

f[lst_?(MatrixQ[#] &)] := Apply[Thread[Rule[#1, #2]] &, lst]
f[lst_List] := Apply[Thread[Rule[#1, #2]] &, lst, {1}]


But I personally never liked this type of coding much. Because now the logic is spread around, instead of all being contained in one place that is easier to see and maintain, So I normally like to have one function, and inside it, add the different logic needs for different values and types of the input, rather than define many different functions.

Yet another way would be this:

Thread[#1 -> #2] & @@@ lst

lst = {{{1, 2}, {a, b}}, {{3, 5}, {4, f}}}


You could also do

MapThread[Rule, lst\[Transpose] , 2]
(* {{1 -> a, 2 -> b}, {3 -> 4, 5 -> f}} *)


Another alternative is:

Thread /@ (Rule @@@ lst)

 Inner[Rule, Sequence @@ #, List] & /@ lst
Apply[Rule, Transpose /@ lst, {2}]
(* {{1 -> a, 2 -> b}, {3 -> c, 4 -> d}} *)


where

 lst = {{{1, 2}, {a, b}}, {{3, 4}, {c, d}}}


EDIT: For inputs with different depths:

  thrdRlF = Apply[Rule, Thread /@ #, {Depth[#] - 2}] &
thrdRlF[lst]
(* {{1 -> a, 2 -> b}, {3 -> c, 4 -> d}} *)
thrdRlF[lst[]]
(* {1 -> a, 2 -> b} *)
thrdRlF[lst[[1,1]]]
(* 1 -> 2 *)
thrdRlF[lst[[1,1,1]]]
(* 1 *)