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.

Drew
  • 75,699
  • 9
  • 109
  • 225
Tohiko
  • 1,589
  • 1
  • 10
  • 22

2 Answers2

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)))
choroba
  • 1,925
  • 10
  • 16
  • 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 '21 at 22:49
  • +1. (But I'd say the former is simpler than the latter.) – Drew Mar 25 '21 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 '21 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.

Basil
  • 12,019
  • 43
  • 69