14
$\begingroup$
data = {{1, a, x, "one"}, {2, b, y, "two"}, {3, c, z, "three"}}

I want to apply a list of four functions

{f, g, h, m}

one for each element of these nested lists respectively. One solution I have come up with is the following:

MapAt[m,
 MapAt[h,
  MapAt[g,
   MapAt[f, data, {All, 1}], {All, 2}], {All, 3}], {All, 4}]
{{f[1], g[a], h[x], m["one"]},
 {f[2], g[b], h[y], m["two"]},
 {f[3], g[c], h[z], m["three"]}}

But I do not consider this an elegant solution because I cannot find a way to escape from the MapAt nested repetition. Could you possibly show me the way to generalize the problem and/or suggest a different answer?

$\endgroup$
2
  • $\begingroup$ Thank you for editing my question, and thank you all for your answers. I think this is a nice problem to demonstrate how powerful functional programming is with Mathematica and how flexible one can be using any of these functions. I have learned a lot from the comparison of all these approaches to the solution of the problem. $\endgroup$ Jul 13, 2016 at 13:16
  • 2
    $\begingroup$ I knew it had the be here already: a list of functions onto a list of (lists of) values $\endgroup$
    – Kuba
    Jul 17, 2016 at 7:52

5 Answers 5

15
$\begingroup$
data = {{1, a, x, "one"}, {2, b, y, "two"}, {3, c, z, "three"}};
functions = {f, g, h, m};

Inner[#2[#1] &, data, functions, List]

(* Out: 
  {{f[1], g[a], h[x], m["one"]}, 
   {f[2], g[b], h[y], m["two"]}, 
   {f[3], g[c], h[z], m["three"]}}
*)

With reversing the order of functions and data, to fully harness the functional style without using slots:

 Inner[Compose, functions, Transpose@data, List]
$\endgroup$
4
  • $\begingroup$ Just what I was going to post myself… :) $\endgroup$ Jul 12, 2016 at 22:08
  • $\begingroup$ Great, a very powerful use of the Inner, and yes that generalizes also the problem, thank you MarcoB $\endgroup$ Jul 12, 2016 at 22:11
  • 1
    $\begingroup$ @march why not an answer? p.s. worth to mention Compose, (28779) $\endgroup$
    – Kuba
    Jul 13, 2016 at 7:21
  • $\begingroup$ @Kuba. Right you are! I have added it to my post. $\endgroup$
    – march
    Jul 13, 2016 at 16:39
5
$\begingroup$

Not quite as nice and concise as the Inner solution, but still worth writing down I think:

MapThread[#1@#2 &, {Table[functions, {Length@data}], data}, 2]
MapThread[Compose, {Table[functions, {Length@data}], data}, 2]

or

MapThread[#1@#2 &, {functions, #}] & /@ data
MapThread[Compose, {functions, #}] & /@ data

or

$\endgroup$
5
$\begingroup$
data // Replace[#, {a_, b_, c_, d_} :> {f@a, g@b, h@c, m@d}, 1] &
$\endgroup$
2
  • $\begingroup$ Thank you @Sascha, that is nice and clean and definitely shorter than mine ;-) $\endgroup$ Jul 12, 2016 at 22:03
  • 3
    $\begingroup$ If the list is four elements long, there will be a problem. Instead, use Replace with a level-spec: Replace[{{1, a, x, "one"}, {2, b, y, "two"}, {3, c, z, "three"}}, {a_, b_, c_, d_} :> {f@a, g@b, h@c, m@d}, 2] $\endgroup$
    – march
    Jul 12, 2016 at 22:13
4
$\begingroup$
Needs["GeneralUtilities`"]

MultiMapAt[Transpose[{ConstantArray[All, #], Range@#}]&[Length@functions], functions][data]
{{f[1], g[a], h[x], m["one"]}, {f[2], g[b], h[y], m["two"]}, 
 {f[3], g[c], h[z], m["three"]}}

Which does the same as

(Composition @@ MapThread[MapAt, {functions, {{All, 1}, {All, 2}, {All, 3}, {All, 4}}}])@data
$\endgroup$
4
$\begingroup$
data = {{1, a, x, "one"}, {2, b, y, "two"}, {3, c, z, "three"}};
functions = {f, g, h, m};

Late for the party:

Transpose[# /@ {##2} & @@@ Transpose @ Prepend[data, functions]]
$\endgroup$
1
  • 1
    $\begingroup$ (+1) ... or ♯ = (# /@ {##2} & @@@ ({#, ## & @@ #2})) &; ♯[functions, data] :) $\endgroup$
    – kglr
    Jul 14, 2016 at 1:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.