I have the following nested alist:
The example does not show a real alist, because its first element,
foo, is not a cons cell. I'd personally call it a tree. Functions like
assoc-string may handle this, others may ignore such elements, but in general alist functions expect every element to be a cons with a car and a cdr. See
(info "(elisp) Lists") and its subnodes.
So far this is what I was able to cook up:
Elisp doesn't handle recursion very efficiently, so I'd recommend avoiding it in general, if possible. You may otherwise hit
unlike q1, I don't know the "path" to the value
Also, I have a larger a list which can have a "depth" of 2 - 5 (by depth I mean alists in alists)
Given the irregularity of this data structure, I'd recommend flattening the list first before looking things up in it. This should greatly simplify the code's complexity at the cost of some time and space. In Emacs 27:
(setq x '(foo
(bar (chocolate . "edible")
(gold . "inedible"))
(jar . "glass")))
(cadr (memq 'chocolate (flatten-tree x))) ; => "edible"
Here's the current implementation of
flatten-tree, in case you're on an older version of Emacs:
(defun flatten-tree (tree)
"Return a \"flattened\" copy of TREE.
In other words, return a list of the non-nil terminal nodes, or
leaves, of the tree of cons cells rooted at TREE. Leaves in the
returned list are in the same order as in TREE.
\(flatten-tree \\='(1 (2 . 3) nil (4 5 (6)) 7))
=> (1 2 3 4 5 6 7)"
(while (consp tree)
(let ((elem (pop tree)))
(while (consp elem)
(push (cdr elem) tree)
(setq elem (car elem)))
(if elem (push elem elems))))
(if tree (push tree elems))
Alternatively, you could perform an iterative depth-first tree search. It trades Elisp's recursion issues for more complex code. Here's an example of DFS on a HTML DOM taken from https://github.com/abo-abo/swiper/pull/1593#issuecomment-392587760 :
(defun counsel--firefox-bookmarks-libxml ()
"Parse current buffer contents as Firefox HTML bookmarks.
Return list of propertized string candidates for
Note: This function requires libxml2 support."
;; Perform iterative pre-order depth-first search instead of using
;; `dom.el' because the latter is new to Emacs 25 and uses recursion.
(let ((stack (cddr (libxml-parse-html-region (point-min) (point-max))))
(while (let ((node (pop stack)))
(if (eq (car-safe node) 'a)
(let* ((text (cl-caddr node))
(attrs (cadr node))
(href (cdr (assq 'href attrs)))
(tags (cdr (assq 'tags attrs))))
(unless (zerop (length href))
(push (counsel--firefox-bookmarks-cand href text tags)
(dolist (child (nreverse (cddr node)))
(when (consp child)
(push child stack))))
In your case the
while loop would additionally terminate on location of the desired key.
Alternatively, I'd recommend structuring your data such that it is more regular. ;)