Search code examples
lispelisp

Intersection of multiple lists in elisp


How to get the intersection of multiple lists using elisp? I'm a elisp newbie but I'm imagining there is some builtin function or a nicer solution using reduce. I cobbled this together, but it seems overly complicated.

;; get the intersection of these lists
;; result should be (3 4 5)
(setq test '((0 1 2 3 4 5) (2 3 4 5 6) (3 4 5 6 7)))

(require 'cl-lib)
(cl-remove-if-not
 (lambda (x) (cl-every
         (lambda (y) (> (length (memq x y) ) 0 ) )
         (cdr test) ) )
 (car test) )
;; ( 3 4 5)

Solution

  • There is a cl-intersection that takes only two operands:

    (cl-intersection '(0 1 2 3 4 5) '(2 3 4 5 6))
    

    You can use it do define your own intersection:

    (defun my-intersection(l)
        (cond ((null l) nil)
              ((null (cdr l)) (car l))
              (t (cl-intersection (car l) (my-intersection (cdr l))))))
    
    (my-intersection '((0 1 2 3 4 5) (2 3 4 5 6) (3 4 5 6 7)))
    

    Updated

    Thanks to the @Tobias comment below, you could have in the new function the same keyword parameters of cl-intersection, that is (:test :test-not :key) and propagate them to all the calls to it inside the recursion.

    Here is the extended version:

    (defun my-intersection(l &rest cl-keys)
        (cond ((null l) nil)
              ((null (cdr l)) (car l))
              (t (apply 'cl-intersection (car l) (apply 'my-intersection (cdr l) cl-keys) cl-keys))))