如何在elisp中按给定索引对列表进行分组?列表表示表,如组织表,因此每个子列表代表一行,例如
| a | 1 | 0 |
| b | 1 | 1 |
| c | 0 | 0 |
将是'((a 1 0) (b 1 1) (c 0 0))
。
我希望能够将另一列的给定列分组。因此,例如,将第一列分组为第三列,我希望'((0 a c) (1 b))
,因为第一行和第三行的第三列为0。
我已经尝试了以下代码,但它创建了很多循环。是否有elisp中的分组功能或更好的方式?
;; group column1 by column2 in table
(defun group-by (col1 col2 table)
(let ((vals (cl-remove-duplicates ;find unique values to group by
(cl-loop for row in table
collect (nth col2 row)))))
(cl-loop for val in vals ;for each unique value
collect (cons val (cl-loop for row in table ;check each row for match
when (eq val (nth col2 row))
collect (nth col1 row))))))
(defvar tst-data '((a 1 0) (b 1 1) (c 0 0)))
(group-by 0 2 tst-data)
;; ((1 b)
;; (0 a c))
答案 0 :(得分:1)
ElispCookbook上恰好有一个函数可以对列表中的元素进行分组,称为group-by-eq
。该函数依次使用一个函数f
,该函数将应用于列表的每个元素(“行”),然后按该值对列表(“行”)进行分组。
使用该函数,我们可以通过传递一个调用nth
的函数来按列函数编写一个组:
(defun group-by-col (n table)
(group-by-eq (lambda (row) (nth n row)) table))
你的问题要求进行双重分组,但让我们只进行一次分组:
(let ((test-data '((a 1 0)
(b 1 1)
(c 0 0))))
(mapcar 'cdr (group-by-col 2 test-data)))
;; (((c 0 0) (a 1 0)) ((b 1 1)))
嗯,我认为有第二个分组,但是看起来你只想选择每个组的第n个元素,在这个例子中是第一个元素:
(let ((test-data '((a 1 0)
(b 1 1)
(c 0 0))))
(mapcar
(lambda (grp)
(cons (car grp) (mapcar (lambda (lst) (nth 0 lst)) (cdr grp))))
(group-by-col 2 test-data)))
;; ((0 c a) (1 b))