xyzzy で重複行を削除する

CSVファイルとか、XMLのタグ抽出したりとかで、何かと必要になることが多いのが重複行の削除。
xyzzy では C-x # uniq して、外部の uniq.exe を使うのがデフォの様子。
だけど、これぐらいの日常タスクなら xyzzy だけでやりたいと思ったから適当に書いといた。

  • 範囲はリージョンで指定する
  • 連続して重複していようが、ばらけていようが、2回以上の出現は重複とみなす
  • ならびは元のデータに合わせる
  • 大文字小文字区別 あり uniq-line-region
  • 大文字小文字区別 なし uniq-line-region-case-insensitive

下のようになる。

(元データ)   uniq-line-region    uniq-line-region-case-insensitive
----------   ----------------    ---------------------------------
AAA          AAA                 AAA
ccc          ccc                 ccc
CCC          CCC                 xxx
aaa          aaa                 BBB
AAA          xxx
aaa          BBB
xxx          bbb
BBB
bbb
ccc
aaa
bbb
aaa
aaa
(defun uniq-line-region (from to &optional case-insensitive)
  "重複行を削除する。case-insensitive を省略するか nil の場合は、大文字小文字の区別をする。
non-nil のときは大文字小文字を区別しない。"
  (interactive "*r")
  (save-excursion (save-restriction
    (narrow-to-region from to)
    (goto-char (point-min))
    (let (l)
      (loop
        (let ((s (buffer-substring (progn (goto-eol) (point)) (progn (goto-bol) (point)))))
          (unless (member s l :test (if case-insensitive #'string-equal #'string=))
            (setq l (cons s l)))
          (unless (forward-line 1) (return))))
      (delete-region from to)
      (with-output-to-selected-buffer
        (map nil #'(lambda (x) (format t "~A~%" x)) (nreverse l)))))))
        
(defun uniq-line-region-case-insensitive (from to)
  "重複行を削除する。大文字小文字を区別しない。"
  (interactive "*r")
  (uniq-line-region from to t))

似たようなタスクとして、リージョンをソートしたいときは下記が参考になる。