Preliminaries
I have lists that are lists of lists (i.e., each list contains one or more sublists and are ragged (i.e., each sublist can have a different length, though all have a length of 1 or greater). However, each list is similarly ragged -- each list has the same number of sublists, and corresponding sublists have the same length.
As an example, consider the lists listA
, listB
, and listC
:
listA = {{A1x}, {A2x, A2y, A2z}, {A3x, A3y}, {A4x, A4y, A4z, A4a}};
listB = {{B1x}, {B2x, B2y, B2z}, {B3x, B3y}, {B4x, B4y, B4z, B4a}};
listC = {{C1x}, {C2x, C2y, C2z}, {C3x, C3y}, {C4x, C4y, C4z, C4a}};
Objective
I wish to write a function myFunction
that maps a function func
across corresponding elements of two or more lists. myFunction
would have the form
myFunction[func, lists__]
I wish to define myFunction
such that the function call
myFunction[f, listA, listB] (* function call 1 *)
returns this output:
(* function call 1 desired output *) { {f[{A1x, B1x}]}, {f[{A2x, B2x}], f[{A2y, B2y}], f[{A2z, B2z}]}, {f[{A3x, B3x}], f[{A3y, B3y}]}, {f[{A4x, B4x}], f[{A4y, B4y}], f[{A4z, B4z}], f[{A4a, B4a}]} }
while the function call
myFunction[f, listA, listB, listC] (* function call 2 *)
returns this output:
(* function call 2 desired output *) { {f[{A1x, B1x, C1x}]}, {f[{A2x, B2x, C2x}], f[{A2y, B2y, C2y}], f[{A2z, B2z, C2z}]}, {f[{A3x, B3x, C3x}], f[{A3y, B3y, C3y}]}, {f[{A4x, B4x, C4x}], f[{A4y, B4y, C4y}], f[{A4z, B4z, C4z}], f[{A4a, B4a, C4a}]} }
where f
can be the built-in function Mean
to compute a simple average.
Additionally, I would like myFunction
to be compatible with Mathematica 9 and later, so myFunction
needs to use functions available in Mathematica 9 or earlier.
Could you please help me think through how to accomplish this? (I am not a student and this is not a homework assignment.)
Attempts at reaching the objective
I've started by considering the first function call, myFunction[f, listA, listB]
, which involves only two lists. Combining Map
and Transpose
gets me seemingly somewhat close to my goal:
myFunction[func_, list1_List, list2_List] := Map[func, Transpose[{listA, listB}];
myFunction[f, listA, listB]
(* output *) { f[{{A1x}, {B1x}}], f[{{A2x, A2y, A2z}, {B2x, B2y, B2z}}], f[{{A3x, A3y}, {B3x, B3y}}], f[{{A4x, A4y, A4z, A4a}, {B4x, B4y, B4z, B4a}}] }
... except that I would need f
to be mapped deeper for sublists with length greater than 1. One (not very elegant) way to do this may be:
myFunction[func_, list1_List, list2_List] := Map[
If[
Length[#[[1]]] == 1, {func[Flatten[#]]},
Map[func, Transpose[#]]
] &,
Transpose[{listA, listB}]];
myFunction[f, listA, listB]
(* output *) { {f[{A1x, B1x}]}, {f[{A2x, B2x}], f[{A2y, B2y}], f[{A2z, B2z}]}, {f[{A3x, B3x}], f[{A3y, B3y}]}, {f[{A4x, B4x}], f[{A4y, B4y}], f[{A4z, B4z}], f[{A4a, B4a}]} }
I imagine there's a much more elegant, compact way to do that, but this appears to work. But even if I use this definition, I still need to generalize it to 3 or more input lists.
How do I do that? I think I need to use the BlankSequence[]
pattern object (two _
characters) for the lists input:
myFunction[func_, lists__] := Map[
If[
Length[#[[1]]] == 1, {func[Flatten[#]]},
Map[func, Transpose[#]]
] &,
Transpose[{lists}]];
myFunction[f, listA, listB, listC]
(* output *) { {f[{A1x,B1x,C1x}]}, {f[{A2x,B2x,C2x}],f[{A2y,B2y,C2y}],f[{A2z,B2z,C2z}]}, {f[{A3x,B3x,C3x}],f[{A3y,B3y,C3y}]}, {f[{A4x,B4x,C4x}],f[{A4y,B4y,C4y}],f[{A4z,B4z,C4z}],f[{A4a,B4a,C4a}]} }
And this is the result I was looking for. Is there a more compact way to do this? MapThread
looks possibly useful, but it's not clear to me how to use for my particular application.
Function[, f[{##}], Listable][listA, listB]
$\endgroup$Function[, f[##] ...
, notFunction[, f[{##}] ...
$\endgroup$A1X
,A2X
etc...) are in fact themselves Lists ( {} ) $\endgroup$MapThread[MapThread[f@*List]@*List, {listA, listB, listC}]
? $\endgroup$f[{A1x, B1x}]
notf[A1x, B1x]
and for the latter case just makingf
Listable
will do the job. $\endgroup$