Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(emacs) lisp: search anything in a ((nested) list)

I need to locate a particular value that can be buried into a deeply nested list, and never at the same place. Or even the same depth ; Here is one form of the list:

(setq my-list '(((partnum . 1) (type (TEXT . plain)) (body (charset UTF-8))
                 (disposition nil) (transfer-encoding QUOTED-PRINTABLE))
                ((partnum . 2) (type (TEXT . html)) (body (charset UTF-8))
                 (disposition nil) (transfer-encoding QUOTED-PRINTABLE)))) 

Now I need to retrieve the value of "charset" ; The first one if any. In this very configuration, it's easy:

(car (cdr (cadr (third (car my-list)))))
   => UTF-8

But this is when I know exactly where the "body" cell is.

I tried to use mapcar recursively like this :

(defun search-rec (list)
  (mapcar
     (lambda (x)
       (if (listp x)
           (search-rec x)
         (message "OY %s" x)))
     list))

but every time, I get the error (wrong-type-argument listp 1) when the recursion hits the first atom of the first cons cell. I guess my question really is what it is :

How can I search in a list?

EDIT Now the list looks like this, "charset" is still in (body) (told you that was about the only constant thing) and it's no longer found :(

(setq my-list '(((partnum . 1)
                (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                     (disposition nil) (transfer-encoding 7BIT))
                (1.2 (type (TEXT . html)) (body (charset UTF-8))
                     (disposition nil) (transfer-encoding 7BIT))
                (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                (disposition nil) (transfer-encoding nil))
               ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                (disposition nil) (transfer-encoding 7BIT))
               ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                (disposition nil) (transfer-encoding 7BIT))
               ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                (disposition nil) (transfer-encoding BASE64))))

EDIT here is some more IRL example:

    (setq my-list haystack-list)
    (setq my-needle (tree-assoc 'charset my-list))
    (message "
-------------\n
- my-list: %s\n
- my-needle: %s\n
-------------\n" my-list my-needle)

Produces:


  • my-list: ((TEXT plain (charset UTF-8) nil nil 7BIT 260 18 nil nil nil) (TEXT html (charset UTF-8) nil nil QUOTED-PRINTABLE 738 17 nil nil nil) alternative (boundary e89a8fb1f8061a6be404c70a24a0) nil nil)

  • my-needle: nil


When on the other hand:

(tree-assoc 'charset '((TEXT plain (charset UTF-8) nil nil 7BIT 260 18 nil nil nil)
(TEXT html (charset UTF-8) nil nil QUOTED-PRINTABLE 738 17 nil nil nil) 
alternative (boundary e89a8fb1f8061a6be404c70a24a0) nil nil))
  =>(charset UTF-8)

So really, I don't know what's going on here : One could argue "what is this haystack-list and where does it come from?" But is it relevant ? I'm working on a copy (my-list) of this haystack-list so what gives those different results ? The quoting of the list ? Guys, I'm really lost

NB (This behaviour (Works in a direct eval, but not in a defun/let production situation) occurred with all the solution given)

EDIT: I ended up extracting the first list found, and then extracting (not searching) elements from that list. I proved faster ; Of course this is when you can say "my element is always in the fist list found) ; thanks to everybody, I learned a lot through all this.

like image 494
yPhil Avatar asked Aug 11 '12 04:08

yPhil


1 Answers

It looks like you want the tree analog of Association Lists. By following the conventions of the assoc function, which retrieves the list element that contains the given key as its head, here is a version of assoc that works on tree:

(defun tree-assoc (key tree)
  (when (consp tree)
    (destructuring-bind (x . y)  tree
      (if (eql x key) tree
        (or (tree-assoc key x) (tree-assoc key y))))))

Example:

(let ((my-list '(((partnum . 1)
                  (1.1 (type (TEXT . plain)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  (1.2 (type (TEXT . html)) (body (charset UTF-8))
                   (disposition nil) (transfer-encoding 7BIT))
                  (type . alternative) (body (boundary e89a8fb2067eba300404c63c5f7f))
                  (disposition nil) (transfer-encoding nil))
                 ((partnum . 1.1) (type (TEXT . plain)) (body (charset UTF-8))
                  (disposition nil) (transfer-encoding 7BIT))
                 ((partnum . 1.2) (type (TEXT . html)) (body (charset UTF-8))
                  (disposition nil) (transfer-encoding 7BIT))
                 ((partnum . 2) (type (IMAGE . x-xpixmap)) (body (name ladybug.xpm))
                  (disposition nil) (transfer-encoding BASE64)))))
  (tree-assoc 'charset my-list))

=> (charset UTF-8)
like image 190
huaiyuan Avatar answered Sep 19 '22 23:09

huaiyuan