4

Is there a function that applies a transformation to a sequence and returns only the non-nil values?

Right now I am using the following (as an example):

(seq-filter #'identity (mapcar (lambda (x) 
                                 (when (< x 3) (+ x 5)))
                               '(1 2 3 4 5 6)))

Or through dolist

(defun mapcar-true (func list)
  (let (value)
    (dolist (elt list value)
      (when-let ((trans (funcall func elt)))
        (setq value (append value (list trans)))))))

But I feel there must be a more straightforward way.

1

4 Answers 4

6

Use remq or delq to remove nil elements from a list (to remove more complex structures, consider remove and delete, too).

(remq nil (mapcar (lambda (x) 
                    (when (< x 3) (+ x 5)))
                  '(1 2 3 4 5 6)))

The first example can be also simplified using flatten-tree as it will remove empty lists, i.e. nils.

(flatten-tree (mapcar (lambda (x) 
                        (when (< x 3) (+ x 5)))
                      '(1 2 3 4 5 6)))
3
  • Please indent your example properly. (A guess is that you pasted code that uses TAB chars for indentation. If so, you'll want to convert them to space chars here. M-x untabify can do that for you in Emacs.)
    – Drew
    Mar 25, 2021 at 22:49
  • +1. (But I'd say the former is simpler than the latter.)
    – Drew
    Mar 25, 2021 at 22:51
  • Thanks for the answer. I thought there must be a solution that does not create an intermediate list with nil values (I should've specified this in my original question). But I guess removing nil values is easy enough.
    – Tohiko
    Mar 26, 2021 at 14:27
1

Is there a function that applies a transformation to a sequence and returns only the non-nil values?

Since Emacs 26, you can use mapcan:

(mapcan (lambda (n)
          (and (< n 3) (list (+ n 5))))
        (number-sequence 1 6))

Alternatively, there's the age-old mapcar+delq approach like in choroba's answer (delq is more commonly used than remq, since mapcar already returns a fresh list, so there's no need to copy it first).

Which of the two approaches to use usually boils down to personal preference and/or performance profiling.

Personally, I'd submit a feature request via M-x report-emacs-bug for such a function to be added to seq.el.

0

seq-filter return a list of all the elements in SEQUENCE for which PRED returns non-nil:

(seq-keep (lambda (x) 
            (when (< x 3) (+ x 5)))
          '(1 2 3 4 5 6))
0

The likes of mapcar are intended to return a list the same length as the original, so post-processing is the only option for anything like that.

Otherwise you need to conditionally add things to your list in the first place. There are lots of options, but I think that iterating with cl-loop is quite nice:

(cl-loop for x in '(1 2 3 4 5 6)
         if (< x 3)
         collect (+ x 5))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.