How can I iterate over a plist, prompt for each key a value and fill in said value into the plist?
I have 'the skeleton' of a list of properties for my projects
(defconst project-properties
'(:number nil
:place nil
:location nil))
I copy this list and fill in the values so I keep a skeleton list and have the filled in list to process it further.
I managed to do it for an alist, but am unable to change the functionality to let it work with plists.
(defun project-prompt-properties-alist ()
"Prompt for project properties and return them"
(let ((properties (copy-alist project-properties)))
(cl-loop for (prop . val) in properties
do (setf (alist-get prop properties) (read-string (format "Geef %s: " prop))))
properties))
Both Common Lisp and Elisp allow you to loop over a list using on
which iterates on successive tails of the list, instead of in
which iterates on successive elements of the list. For example:
CL-USER> (loop for tail on project-properties
do (format t "~A~%" tail))
(NUMBER NIL PLACE NIL LOCATION NIL)
(NIL PLACE NIL LOCATION NIL)
(PLACE NIL LOCATION NIL)
(NIL LOCATION NIL)
(LOCATION NIL)
(NIL)
You can further use the by
keyword to reduce the input list by two elements instead of one on each iteration:
CL-USER> (loop for tail on project-properties by #'cddr
do (format t "~A~%" tail))
(NUMBER NIL PLACE NIL LOCATION NIL)
(PLACE NIL LOCATION NIL)
(LOCATION NIL)
You can use destructuring to take the first two elements of each tail:
CL-USER> (loop for (prop val) on project-properties by #'cddr
do (format t "~A -> ~A~%" prop val))
NUMBER -> NIL
PLACE -> NIL
LOCATION -> NIL
These features work the same in Elisp. You should be able to write project-prompt-properties-plist
for Elisp like this:
(defun project-prompt-properties-plist ()
"Prompt for project properties and return them"
(let ((properties (copy-sequence project-properties)))
(cl-loop for (prop val) on properties by #'cddr
do (plist-put properties prop (read-string (format "Geef %s: " prop))))
properties))