Zip up two lists of different lengths in elisp

I have two lists:

(setq x (list "a" "b" "c")) (setq y (list "1" "2" "3" "4")) 

How to create a list of cells cons (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4")) with a shortened list?

+6
source share
4 answers

Here is my trick:

 (require 'cl-lib) (cl-mapcar #'list (setcdr (last x) x) y) 

I would add a check, which one is bigger, but this will ruin the brevity :).

+6
source

Of course, there is an easier way to do this, but here is a version that turns input sequences into endless lists and fastens them together:

 (defun* cycle-iterator (xs &optional (idx 0) (len (length xs))) "Create an iterator that will cycle over the elements in XS. Return a cons, where the car is the current value and the cdr is a function to continue the iteration." (cons (nth (mod idx len) xs) (eval `(lambda () (cycle-iterator ',xs ,(1+ idx) ,len))))) (defun cycle-take (xs n) "Take N elements from XS, cycling the elements if N exceeds the length of XS." (loop when (plusp n) ;; Creating the iterator returns the first value. Subsequent calls can then ;; be processed in a loop. with (value . iterator) = (cycle-iterator xs) with acc = (list value) repeat (1- n) do (destructuring-bind (val . next) (funcall iterator) (setq iterator next) (setq acc (cons val acc))) finally (return (nreverse acc)))) (defun cycling-zip (xs ys) "Zip XS and YS together, cycling elements to ensure the result is as long as the longest input list." (loop with limit = (max (length xs) (length ys)) for x in (cycle-take xs limit) for y in (cycle-take ys limit) collect (cons xy))) ;; Usage: (cycling-zip '("a" "b" "c") '("1" "2" "3" "4")) ; => (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4")) 
+1
source

This answer requires a dash list manipulation library. Before attacking your problem, it is helpful to find the length of the longest list. The first way I came is to:

 (require 'dash) (require 'dash-functional) (length (-max-by (-on '> 'length) (list xy))) ; 4 

-on is a smart function from the dash-functional package that takes a comparator, a key for comparison and returns which is compared with this key. Therefore (-max-by (-on '> 'length) xs) finds an element in xs whose length is large. But this expression is too clever for itself, and dash-functional only works in Emacs 24 because of lexical reach. Let's rewrite it, inspired by the Python solution :

 (-max (-map 'length (list xy))) ; 4 

To take the first n elements from an infinite loop list do (-take n (-cycle xs)) . Therefore, to create an alist, where elements from the smaller list are cyclical, write:

 (let ((len (-max (-map 'length (list xy))))) (flet ((cycle (xs) (-take len (-cycle xs)))) (-zip (cycle x) (cycle y)))) ; (("a" . "1") ("b" . "2") ("c" . "3") ("a" . "4")) 
+1
source

I went with a recursive approach that seemed natural to lisp.

 (defun zip (xs ys) (cond ((or (null xs) (null ys)) ()) (t (cons (cons (car xs) (car ys)) (zip (cdr xs) (cdr ys)))))) 
0
source

Source: https://habr.com/ru/post/950024/


All Articles