5

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?

JaM
  • 170
  • 7

3 Answers3

10

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]}]
Sasha
  • 7,373
  • 36
  • 47
7

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]}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    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? – Mr.Wizard Dec 23 '12 at 03:11
  • Sure. By the way, was this useful? – Rojo Dec 23 '12 at 06: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. – Mr.Wizard Dec 23 '12 at 11:25
2

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]}}*)
kglr
  • 394,356
  • 18
  • 477
  • 896