Search code examples
lispautolisp

AutoLISP: Removing consecutive duplicates in list


I've been looking for an efficient way of removing consecutive duplicates in a list of points.

My original thinking was to loop through the elements of the list making a comparison with the (n-1)th element and removing it if its equal. But then it is not trivial to remove an element in a list and using another function would make it inefficient.

I am aware of Lee Mac's RemoveOnce function but I don't know how to modify it to make a comparison between consecutive elements of a list.

The goal as an example would be the following:

List = (p1 p2 p3 p3 p3 p2 p2 p4)

List_without_consecutive_duplicates = (p1 p2 p3 p2 p4)

Thanks!


Solution

  • Here's an iterative method:

    (defun remcondupes ( l / r )
        (while l
            (if (not (equal (car l) (cadr l) 1e-8))
                (setq r (cons (car l) r))
            )
            (setq l (cdr l))
        )
        (reverse r)
    )
    

    And here's a recursive method:

    (defun remcondupes ( l )
        (if l
            (if (equal (car l) (cadr l) 1e-8)
                (remcondupes (cdr l))
                (cons (car l) (remcondupes (cdr l)))
            )
        )
    )
    

    In both of the above, the first element in the list is compared to the second using the equal function with a tolerance of 1e-8 (since we're comparing points), with the first element discarded if this test is validated.

    Testing:

    _$ (setq p1 '(1.2 2.3) p2 '(3.4 4.5) p3 '(5.6 6.7) p4 '(7.8 8.9))
    (7.8 8.9)
    _$ (setq lst (list p1 p2 p3 p3 p3 p2 p2 p4))
    ((1.2 2.3) (3.4 4.5) (5.6 6.7) (5.6 6.7) (5.6 6.7) (3.4 4.5) (3.4 4.5) (7.8 8.9))
    _$ (remcondupes lst)
    ((1.2 2.3) (3.4 4.5) (5.6 6.7) (3.4 4.5) (7.8 8.9))
    

    EDIT:

    Alternatively, to account for consecutive points each successively within the comparison tolerance (per Will's comments below), you might consider the following variations:

    (defun remcondupes ( l / r )
        (while l
            (if (equal (car l) (cadr l) 1e-8)
                (setq l (cons (car l) (cddr l)))
                (setq r (cons (car l) r)
                      l (cdr l)
                )
            )
        )
        (reverse r)
    )
    
    (defun remcondupes ( l )
        (if l
            (if (equal (car l) (cadr l) 1e-8)
                (remcondupes (cons (car l) (cddr l)))
                (cons (car l) (remcondupes (cdr  l)))
            )
        )
    )