Emacs lisp list grouping

Let say I have this list.

'((name (tag1))
  (name2 (tag2))
  (name3 (tag1))
  (name4 (tag3))
  (name5 (tag2 tag1)))

How do I get this?

'((tag1 (name name3 name5))
  (tag2 (name2 name5))
  (tag3 (name4)))

This is for my blog: Tagged view (experimental)

1 Like

I think this could do the trick:

(progn
  (setq articles '((name (tag1))
		   (name2 (tag2))
		   (name3 (tag1))
		   (name4 (tag3))
		   (name5 (tag2 tag1)))
	the-tags '()
	tags-list '()
	tags-listp '())
  (mapcar (lambda (entry)
	    (let* ((title (car entry))
		   (tags (cadr entry)))
	      (message "Processing %S" entry)
	      (message "  title %S" title)
	      (message "  tags  %S" tags)
	      (mapcar (lambda (tag)
			(add-to-list 'the-tags tag :append)
			(let ((tag-list (plist-get tags-listp tag)))
			  (message "  tag-list(%S)  %S" tag tags-listp)
			  (setq tags-listp (plist-put tags-listp tag (append `(,title) tag-list)))))
		      tags)))
	  articles)
  the-tags
  (mapcar
   (lambda (tag)
     (add-to-list 'tags-list `(,tag ,(plist-get tags-listp tag))))
   the-tags)
  tags-list)

Most surely it is not the most efficient way to code this, but it works.

1 Like

This is what I wrote Taxy for: GitHub - alphapapa/taxy.el: Programmable taxonomical hierarchies for arbitrary objects

Here’s an example of doing it with your sample code:

(let* ((articles '((name (tag1))
                   (name2 (tag2))
                   (name3 (tag1))
                   (name4 (tag3))
                   (name5 (tag2 tag1))))
       (tags (delete-dups
              (flatten-list (mapcar #'cadr articles))))
       (taxy (make-taxy
              :name "Blog articles"
              :taxys (mapcar (lambda (tag)
                               (make-taxy :name (symbol-name tag)
                                          :predicate (lambda (article)
                                                       (member tag (cadr article)))
                                          :then #'identity))
                             tags))))
  (setf taxy (thread-last taxy
                          taxy-emptied
                          (taxy-fill articles))
        (taxy-items taxy) nil)
  (taxy-plain taxy))

That produces:

("Blog articles"
 (("tag1" ((name5 (tag2 tag1))
           (name3 (tag1))
           (name (tag1))))
  ("tag2" ((name5 (tag2 tag1))
           (name2 (tag2))))
  ("tag3" ((name4 (tag3))))))
3 Likes

My attempt:

(let* ((data '((name (tag1))
               (name2 (tag2))
               (name3 (tag1))
               (name4 (tag3))
               (name5 (tag2 tag1)))))
  (mapcar (lambda (tag)
            `(,tag ,(delete nil
                            (mapcar (lambda (item)
                                      (when (member tag (cadr item))
                                        (car item)))
                                    data))))
          (delete-dups (flatten-tree (mapcar #'cdr data)))))
2 Likes

This made sense to me so I adopted it.

1 Like

I think you can use a hashtable or plist for the second list (the one you want to make) then you don’t have to worry about duplicate keys. and will have one less mapcar!

with plist:

(let ((l '((name (tag1))
          (name2 (tag2))
          (name3 (tag1))
          (name4 (tag3))
          (name5 (tag2 tag1))))
      (pl (list)))
(mapcar (lambda (item) 
          (mapcar (lambda (tag) 
                    (setq pl (plist-put pl tag (append (list (car item)) (plist-get pl tag)))))
                  (cadr item)))
        l)
pl
)

but personally I’d prefer a hashtable:

(let ((data '((name (tag1))
          (name2 (tag2))
          (name3 (tag1))
          (name4 (tag3))
          (name5 (tag2 tag1))))
      (table (make-hash-table :test 'equal)))
(mapcar (lambda (item) 
          (mapcar (lambda (tag) 
                    (puthash tag (append (list (car item)) (gethash tag table))
                             table)) 
                  (cadr item)))
        data)
table
)

I think you can use a hashtable or plist for the second list (the one you
want to make) then you don’t have to worry about duplicate keys. and will
have one less mapcar!

I will make sure to take a look at it, the current solutions works fine
but it has 2 mapcars as you say.