GROUP BY + COUNT sobre líneas en una región

7

¿Cuál es la forma más simple de obtener el número de distintos repetidos en una región?

Por ejemplo, de

THIS IS LINE A
THIS IS LINE A
THIS IS LINE A
THIS IS LINE B
THIS IS LINE B
THIS IS LINE C

Me gustaría conseguir

THIS IS LINE A    3
THIS IS LINE B    2
THIS IS LINE C    1

La salida podría realizarse sobre la misma región (reemplazando la selección actual).

rsenna
fuente

Respuestas:

10

En Linux, y supongo que Mac, puede canalizar la región a través del uniqcomando de shell para obtener casi exactamente lo que desea.

  1. Marcar la region

  2. Ordenar las líneas con M-x sort-lines

  3. Llame shell-command-on-regioncon la tecla de prefijo:C-u M-|

  4. Entrar uniq --count

El contenido del búfer será reemplazado por:

  3 THIS IS LINE A
  2 THIS IS LINE B
  1 THIS IS LINE C

Puede automatizar aún más esto con macros de teclado, etc., pero esto puede ser lo suficientemente bueno como es.

EDITAR: como señala @phils, puede ordenar con un comando de shell en lugar de con la función Emacs. En este caso, suelte el paso 2, y para el paso 4 ingrese en sort | uniq -clugar de solo uniq -c.

Tyler
fuente
¡Agradable! En una Mac, uniqtiene la -copción de anteponer recuentos, y no creo que deba ordenar antes de usar uniq. (Además, el OP solicitó procesar la región , no todo el búfer).
Constantine
Gracias. En Linux -cy --countson sinónimos, y es necesario ordenarlos, pero tal vez la versión de Mac usa valores predeterminados diferentes. ¡Corregiré el paso 1!
Tyler
Acabo sshde entrar en una caja corriendo Ubuntu 14.04.1 LTS: todavía no se necesita ordenar para mí.
Constantine
1
Tyler:C-u M-| sort | uniq -c
phils
1
Ugh Soy demasiado lento para editar comentarios. Esto es lo que pretendía decir: "@rsenna: Tú eres quien planteó la pregunta; me alegra saber que funcionó para ti. (No me importan los puntos de reputación; aprecio un +1, pero estoy totalmente de acuerdo que mi respuesta no da la "forma más simple". ""
Constantine
5

Veo tres tareas aquí:

  1. Obtenga una lista de líneas en una región, sin duplicados.
  2. Para cada línea de esta lista, cuente cuántas veces ocurrió en la región original y recopile esta información.
  3. Insertar el resumen.

 

(defun uniqify-lines (beg end)
  "Return a list of lines in a region (without duplicates). Omit empty lines."
  (let ((text (buffer-substring beg end)))
    (with-temp-buffer
      (insert text)
      (delete-duplicate-lines (point-min) (point-max))
      (split-string (buffer-string) "\n" t))))

(defun count-duplicates (beg end)
  "Count duplicate lines in a region. Returns a list of the
    form ((line . count) ...)."
  (mapcar (lambda (str)
            (cons str (how-many (regexp-quote str) beg end)))
          (uniqify-lines beg end)))

(defun insert-line-stats (beg end)
  "Remove duplicate lines in the region. Append the number of
    occurences to each line in the result. Replaces current region."
  (interactive "r")
  (let ((stats (count-duplicates beg end)))
    (kill-region beg end)
    (mapc (lambda (line)
            (insert (format "%s %d\n" (car line) (cdr line))))
          stats)))
Constantina
fuente
No lo sabía how-manyni delete-duplicate-linesexistía, ¡a veces parece que puedes unir palabras en inglés con guiones y Emacs sabe qué hacer! Sospecho que también hay una versión incorporada de Emacs uniq, pero no la encontré.
Tyler
2
Esta es una muy buena respuesta. Y dado que no depende de ningún comando externo, también funciona en Windows.
rsenna