根据值将列表列表转换为不同的列表列表

时间:2013-03-27 12:51:28

标签: emacs lisp elisp reduce

我想改变这些列表:

(setq terms '((("name" . "t1c1")
               ("taxonomy" . "category"))
              (("name" . "t1c2")
               ("taxonomy" . "category"))
              (("name" . "t1k1")
               ("taxonomy" . "post_tag"))
              (("name" . "t1k2")
               ("taxonomy" . "post_tag"))
              (("name" . "t1k3")
               ("taxonomy" . "post_tag"))))

进入另一个列表清单:

(("category" "t1c1" "t1c2")
 ("post_tag" "t1k1" "t1k2" "t1k3"))

我想出了:

(reduce
 '(lambda (lists term)
    (let* ((name (cdr (assoc "name" term)))
           (taxonomy (cdr (assoc "taxonomy" term)))
           (existing (assoc taxonomy lists)))
      (if existing
          (progn
            (setcdr existing (sort (cons name (cdr existing)) 'string<)))
        (push (list taxonomy name) lists)))
    lists)
 terms
 :initial-value nil)

看起来不太优雅---使用let *和if语句看起来都像潜在的代码气味。

我很感激任何更好的方法在elisp中做到这一点的例子 - 更好的意思是更纯粹的功能,更好地使用内置函数来表示某些操作等。

哦,我希望对得到的alists的内容进行排序---这使我更容易测试输出。

2 个答案:

答案 0 :(得分:2)

最让人想到的是使用(loop ...)宏。有人可能会认为它不够灵敏,但我认为它的简洁性和表现力胜过纯洁:

(loop
 with result = '()
 for term in terms
 for name = (aget term "name")
 for taxonomy = (aget term "taxonomy")
 do (aput 'result taxonomy
          (sort (cons name (aget result taxonomy)) 'string<))
 finally return result)

答案 1 :(得分:1)

  

我很感激有任何更好的方法可以做到这一点   elisp ---哪里更好可能意味着更纯粹的功能,更好地利用   内置函数来表示某些操作等。

也许是这样的(假设加载了cl-lib):

(cl-flet ((step (lists term)
            (let ((name (aget term "name"))
                  (taxonomy (aget term "taxonomy")))
              (cl-acons taxonomy
                        (sort (cons name (aget lists taxonomy)) #'string<)
                        (cl-remove taxonomy lists :key #'car :test #'equal)))))
  (reduce #'step dhl-terms :initial-value nil))

它的效率低于你的解决方案,因为“更纯粹的功能”也意味着不使用setcdr - 它将包含新条目,并再次传递以删除旧条目。