4

I need to combine two lists elementwise.

Given two lists that look something like,

(setq qux '("foo" "bar" "baz"))
(setq quux '("xyzzy" "thud" "blat"))

I need to use each corresponding element to form a new list,

("foo:xyzzy" "bar:thud" "baz:blat")

I could create a function that defines each element of the desired list,

(defun combiner (a b)
  (format "%s:%s" a b))

but then its not clear how I would pass the base lists in.

Another approach might be to combine the base lists into one, maybe an alist, and then pass that into a combiner function via mapcar. The trouble is, I'm not sure how to combine the two lists elementwise. Thoughts?

Drew
  • 75,699
  • 9
  • 109
  • 225
Lorem Ipsum
  • 4,327
  • 2
  • 14
  • 35

4 Answers4

5

See seq-mapn.

(setq qux '("foo" "bar" "baz"))
(setq quux '("xyzzy" "thud" "blat"))

(seq-mapn #'(lambda (a b) (format "%s:%s" a b)) qux quux)

Resulting in:

("foo:xyzzy" "bar:thud" "baz:blat")

If you prefer to use common-lisp:

(cl-mapcar #'(lambda (a b) (format "%s:%s" a b)) qux quux)
Phil Hudson
  • 1,651
  • 10
  • 13
ideasman42
  • 8,375
  • 1
  • 28
  • 105
2

The solution given by @ideasman42 must obviously be preferred, but just a remark about that part:

I could create a function that defines each element of the desired list, but then its not clear how I would pass the base lists in.

Actually, if you have only two lists, and if you are sure that they have the same length (you should check that either before calling the function, or inside the function), some (ugly) homemade elisp code like that will work, using your combiner function:

(defun combine-listwise (x y)
  (let (result)
    (while (and x y)
      (push (combiner (pop x) (pop y))
            result))
    (nreverse result)))

(combine-listwise qux quux)

That's not very elegant, but I think you had a good idea when you started with this combiner function, and it seems interesting to show how you could use it. :)

wasamasa
  • 21,803
  • 1
  • 65
  • 97
Philopolis
  • 1,094
  • 8
  • 14
  • Indeed, most solutions here are writing in the lambda; I’m not an elisper, but I would expect something like `(seq-mapn combiner qux quux)` (or any other variant) to work just fine. – D. Ben Knoble Mar 12 '21 at 14:32
  • @D.BenKnoble You're correct. `combiner` is what some people might call `#'(lambda (a b) (format "%s:%s" a b))`. :) (A lambda is simply an unnamed function. People are using it because the purpose of `combiner` is probably not significant enough to warrant a dedicated symbol. The `#` indicates to the byte-compiler that the following expression can be byte compiled.) – Lorem Ipsum Mar 12 '21 at 20:46
  • @LoremIpsum yeah, that wasn't my confusion; I'm familiar enough with lisps/functional languages. My point was that this is the only answer that re-used the fact that the function (lambda, if you prefer) was already defined; no need to spell it out again. – D. Ben Knoble Mar 12 '21 at 21:21
2

Another approach is to use cl-loop like this

(cl-loop for e1 in qux for e2 in quux collect (format "%s:%s" e1 e2))

related to cl-map given by @ideasman42 is a little shorter version with cl-mapcar.

(cl-mapcar (lambda (x y) (format "%s:%s" x y)) qux quux)
John Kitchin
  • 11,555
  • 1
  • 19
  • 41
1

dash.el provides some useful functions: -zip, -zip-lists, -zip-with etc E.g.

  (setq qux '("foo" "bar" "baz"))
  (setq quux '("xyzzy" "thud" "blat"))


  (-zip qux quux)
-> (("foo" . "xyzzy") ("bar" . "thud") ("baz" . "blat"))

  (-zip-lists qux quux)
-> (("foo" "xyzzy") ("bar" "thud") ("baz" "blat"))

  (-zip-with (lambda (s1 s2) (format "%s:%s" s1 s2)) qux quux)
-> ("foo:xyzzy" "bar:thud" "baz:blat")
NickD
  • 27,023
  • 3
  • 23
  • 42