5
$\begingroup$

Given two lists $l_a = \{a_1, a_2, a_3\}$, $l_b = \{b_1, b_2, b_3, b_4\}$ and some function $f$ accepting two arguments, how can I produce a list of all values $f(a, b)$ for $a\in l_a$ and $b \in l_b$ using functional construction?

$\endgroup$
6
  • $\begingroup$ I would like to get {f[a1,b1],f[a1,b2],f[a1,b2],...,f[a3,b1],...,f[a3,b4]} (eg. all possible pairs) $\endgroup$
    – JaM
    Dec 21, 2012 at 20:22
  • 3
    $\begingroup$ Use Outer[f,la, lb] where la and lb are your lists $\endgroup$
    – rm -rf
    Dec 21, 2012 at 20:22
  • $\begingroup$ OK - extra question just for fun. How would you implement Outer using functional style? $\endgroup$
    – JaM
    Dec 21, 2012 at 20:32
  • $\begingroup$ @JarekMiszczak Something like f[#, k] & /@ {x1,x2}/. k -> # & /@ {y1, y2, y3}? $\endgroup$ Dec 21, 2012 at 20:45
  • $\begingroup$ @belisarius Thanks for this anwser - I especially like the trick with /. k -> # & $\endgroup$
    – JaM
    Dec 21, 2012 at 21:07

3 Answers 3

10
$\begingroup$

You could do Outer[f, la, lb] as already suggested. Alternatively you could do

Partition[Apply[f, Tuples[{la, lb}], {1}], Length[la]]

or

Table[f[ai,bi],{ai,la},{bi,lb}]

or

Array[f[la[[#1]], lb[[#2]]] &, {Length[la], Length[lb]}]
$\endgroup$
2
  • 1
    $\begingroup$ As for me the second option is great - I'm using list of matrices and I never thought about using them as iterators... $\endgroup$
    – JaM
    Dec 21, 2012 at 20:52
  • $\begingroup$ @JarekMiszczak That's a great and often overlooked syntax salt $\endgroup$ Dec 21, 2012 at 22:49
7
$\begingroup$

While Outer is the canonical function, in addition to the methods that Sasha shows you could use:

Distribute

lst1 = {1, 3, 5, 7};
lst2 = {2, 4, 6, 8};

Distribute[f[lst1, lst2], List]
{f[1, 2], f[1, 4], f[1, 6], f[1, 8], f[3, 2], f[3, 4], f[3, 6], 
 f[3, 8], f[5, 2], f[5, 4], f[5, 6], f[5, 8], f[7, 2], f[7, 4], 
 f[7, 6], f[7, 8]}

Through and Listable

SetAttributes[f, Listable]
f[a__][x_] := f[a, x]

f[lst1][lst2] // Through

Thread and Map

g[a__][x_List] := Thread @ g[a, #] & /@ x

g[lst1][lst2]

ReplaceList

In a comment Rojo suggested using ReplaceList which is quite elegant:

ReplaceList[{lst1, lst2}, {{___, a_, ___}, {___, b_, ___}} :> {a, b}]

Recursion

h[{a_, b__}, x___] := h[{b}, x] ~Prepend~ h[x, a]
h[{a_}, x___] := {h[x, a]}

h[lst1, lst2]

I rather like this last one. It works with multiple lists too:

h[{1, 2, 3}, {a, b}, {x, y}]
{{{h[1, a, x], h[1, a, y]}, {h[1, b, x], h[1, b, y]}},
 {{h[2, a, x], h[2, a, y]}, {h[2, b, x], h[2, b, y]}},
 {{h[3, a, x], h[3, a, y]}, {h[3, b, x], h[3, b, y]}}}

Formulated using linked lists for higher performance:

h[{a_, b__}, x___] := {h[x, a], h[{b}, x]}
h[{a_}, x___] := h[x, a]

h[{1, 2, 3}, {a, b}, {x, y}] // Flatten
{h[1, a, x], h[1, a, y], h[1, b, x], h[1, b, y],
 h[2, a, x], h[2, a, y], h[2, b, x], h[2, b, y],
 h[3, a, x], h[3, a, y], h[3, b, x], h[3, b, y]}
$\endgroup$
4
  • 1
    $\begingroup$ If the intent doesn't imply nesting them, a nice one is also ReplaceList[{{1, 2, 3}, {a, b}}, {{___, a_, ___}, {___, b_, ___}} :> {a, b}] $\endgroup$
    – Rojo
    Dec 22, 2012 at 15:40
  • $\begingroup$ @Rojo Similar to this answer which as you'll note I liked. May I include that in my answer? $\endgroup$
    – Mr.Wizard
    Dec 23, 2012 at 3:11
  • $\begingroup$ Sure. By the way, was this useful? $\endgroup$
    – Rojo
    Dec 23, 2012 at 6:22
  • $\begingroup$ @Rojo for some reason I'm not getting @notifications today. I looked at that answer briefly but I intended to return to it. In the context of a larger Grid layout I believe that different sections will not align. Sorry for not being responsive to your answer without prompting. $\endgroup$
    – Mr.Wizard
    Dec 23, 2012 at 11:25
2
$\begingroup$

ListConvolve

 l1 = {a1, a2, a3}; l2 = {b1, b2, b3};
 Union @@ ListConvolve[l1, l2, 1, l2, f, List]
 (* {f[a1, b1], f[a1, b2], f[a1, b3], 
     f[a2, b1], f[a2, b2], f[a2, b3], 
     f[a3, b1], f[a3, b2], f[a3, b3]} *)

Tuples - an alternative usage

  Tuples[f[l1, l2]]
  (* {f[a1, b1], f[a1, b2], f[a1, b3], 
     f[a2, b1], f[a2, b2], f[a2, b3], 
     f[a3, b1], f[a3, b2], f[a3, b3]} *)

To partition the flat lists above, use Partition or the new-in-Version-9 ArrayReshape:

 ArrayReshape[%, {Length@l1, Length@l2}] 
 (* {{f[a1, b1], f[a1, b2], f[a1, b3]}, 
     {f[a1, b4], f[a2, b1], f[a2, b2]}, 
     {f[a2, b3], f[a2, b4], f[a3, b1]}}*)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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