# All values for a function with two arguments without Outer

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?

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

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]}]

• As for me the second option is great - I'm using list of matrices and I never thought about using them as iterators...
– JaM
Dec 21 '12 at 20:52
• @JarekMiszczak That's a great and often overlooked syntax salt Dec 21 '12 at 22:49

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]}

• If the intent doesn't imply nesting them, a nice one is also ReplaceList[{{1, 2, 3}, {a, b}}, {{___, a_, ___}, {___, b_, ___}} :> {a, b}]
– Rojo
Dec 22 '12 at 15:40
• @Rojo Similar to this answer which as you'll note I liked. May I include that in my answer? Dec 23 '12 at 3:11
– Rojo
Dec 23 '12 at 6:22
• @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. Dec 23 '12 at 11:25

### 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]}}*)