Map each element of a list over every element of another list

Let's assume I've got two lists: ("A" "B" "C" "D") and ("+" "-"). I want to concatenate each element of the first list with every element of the second to get ("A+" "A-" "B+" "B-" ... ). Is there a proper function to do that, e.g. (map-each 'concat list1 list2) => list3?

I came to a solution using nested mapcar calls and reduce but I think I may be missing something since it doesn't seem to be very uncommon task.

(reduce 'append
(mapcar
(lambda (x)
(mapcar
(lambda (y)
(concat x y))
'("+" "-")))
'("A" "B" "C" "D")))

I am not aware of any Elisp built-in function that calls a function on the cartesian product of its list arguments and returns the collected result.

But I like the general map-cartestian-product from an answer at stackoverflow quite much.

Since that is a nice general method I've generalized it a bit more to an arbitrary number of sets:

(defmacro map-cartesian-product-recursion (fun vars lists)
"Apply (FUN x1 ... xn) for all tuples with xi element of (nth i LISTS)."
(if (car lists)
(let ((x (make-symbol "x")))
`(mapc
(lambda (,x)
,(macroexpand `(map-cartesian-product-recursion ,fun ,(cons x vars) ,(cdr lists))))
,(car lists)))
`(funcall ,fun ,@(nreverse vars))))

(defmacro map-cartesian-product (fun &rest lists)
"Apply (FUN x1 ... xn) for all tuples with xi element of (nth i LISTS)."
`(map-cartesian-product-recursion ,fun nil ,lists))

You can use map-cartesian-product for your purpose as follows:

(let (ret)
(map-cartesian-product
(lambda (letter sign)
(setq ret (cons (concat letter sign) ret)))
'("A" "B" "C" "D")
'("+" "-"))
(nreverse ret))

The advantage with respect to your proposal is that you do not construct intermediate lists but only one return list.

• Nice answer; I am not sure the explicit call to macroexpand is necessary, it looks like just expanding into a call to map-cartesian-product-recursion sould be enough. Sep 4 '19 at 9:26