1

如何按 elisp 中的给定索引对列表列表进行分组?列表代表表,如 org-tables,因此每个子列表代表一行,例如。

| 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))
4

1 回答 1

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))
于 2017-04-11T06:41:52.820 回答