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.