リスト内の重複を報告しますか?


7

Q:リスト内の重複する要素のみを取得するにはどうすればよいですか?

delete-dups(およびcl-delete-duplicates)リストからすべての重複要素を削除します。

(delete-dups '(a b c c d d))            ; => '(a b c d)

逆が欲しい:リストの重複のみを返す関数はありますか?

(mystery-function '(a b c c d d))       ; => '(c d)

回答:


6

私は最も簡単な方法はハッシュテーブルを使うことだと思います:

(defun get-duplicates (list &optional test)
  (let ((ht (make-hash-table :test (or test #'equal))) 
        ret)
    (dolist (x list)
      (incf (gethash x ht 0)))
    (maphash (lambda (key value)
               (when (> value 1)
                 (push key ret)))
             ht)
    ret))
(get-duplicates '(a 2 a b 3 2))
==> (2 a)

Fwiwキーワード引数を受け入れるスター付きバージョンのdefunがあります。
YoungFrog 2017年

6

ダッシュを使用:

(defun find-duplicates (list)
  "Return a list that contains each element from LIST that occurs more than once."
  (--> list
       (-group-by #'identity it)
       (-filter (lambda (ele) (> (length ele) 2)) it)
       (mapcar #'car it)))

簡単なテストスイート:

(ert-deftest nothing ()
  (should-not (find-duplicates '())))

(ert-deftest no-duplicates ()
  (should-not (find-duplicates '(1 2 3 4 5 6 7 "eight"))))

(ert-deftest single-duplicate ()
  (should (equal (find-duplicates '(1 2 3 4 1))
                 '(1))))

(ert-deftest multiple-duplicates ()
  (should (equal (sort (find-duplicates '(1 2 3 4 1 6 7 8 9 2))
                       #'<)
                 '(1 2))))

(ert-deftest string-duplicates ()
  (should (equal (find-duplicates '(1 2 "three" 4 "three"))
                 '("three"))))

現在、各重複の最初の発生順にアイテムを返すようです-group-byが、それを保証するものは何も見当たらないので、信頼できるとは思いません。ハッシュテーブルを使用すると、より効率的になる可能性がありますが、これは機能します。


3

ここに非ハッシュバージョンがあります:

#+BEGIN_SRC emacs-lisp
(defun find-duplicates (list)
  (loop for (item . count) in (let ((counts '())
                    place)
                (dolist (el list)
                  (setq place (assoc el counts))
                  (if place
                      (incf (cdr place))
                    (push (cons el 1) counts)))
                counts)
    if (> count 1)
    collect item))
#+END_SRC

0

delete-dupsを使用して反転... delete-dups(およびseq):

(defun report-dups (list)
  (delete-dups (seq-filter
                (lambda (el) (member el (cdr (member el list))))
                list)))

0

これは@caseneuveの定義に似ています。

(defun report-dups (xs)
  (delete-dups (cl-remove-if-not (lambda (x) (member x (cdr (member x xs)))) xs)))

しかし、すでにテスト済みの場合でも、リスト内の各要素をテストする必要があります。そして、彼らは走りdelete-dupsます。

この定義は単純明快であり、非効率性に悩まされることはありません。

(defun report-dups (xs)
  (let ((ys  ()))
    (while xs
      (unless (member (car xs) ys) ; Don't check it if already known to be a dup.
        (when (member (car xs) (cdr xs)) (push (car xs) ys)))
      (setq xs  (cdr xs)))
    ys))

また、ハッシュテーブルソリューションよりも約6倍高速であるget-duplicatesようです(上記)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.