;; Licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams
;; Created: October 14, 2022
;;
;; While obvious, GNU Emacs does not include this file or project.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; /Users/howard.abrams/other/hamacs/ha-data.org
;; And tangle the file to recreate this one.
;;
;;; Code:
#+end_src
* Introduction
Once upon a time, I [[https://www.youtube.com/watch?v=HKJMDJ4i-XI][gave a talk]] to EmacsConf 2019, about [[http://howardism.org/Technical/Emacs/piper-presentation-transcript.html][an interesting idea]] I called [[https://gitlab.com/howardabrams/emacs-piper][emacs-piper]]. I still like the idea of sometimes editing an Emacs buffer on the entire contents, as if it were a data file. This file contains what I feel are the best functions for that… oh, and a leader to call it (instead of the original Hydra).
#+begin_src emacs-lisp
(ha-leader
"d" '(:ignore t :which-key "data")
"d |" '("pipe to shell" . replace-buffer-with-shell-command)
The string replacement functions operate at the current point, which means I need to jump to the beginning before calling something like [[help:vr/replace][vr/replace]].
The [[help:sort-lines][sort-lines]] is useful, but insists on an /active/ region. Let’s make a collection of data-focused versions that work on both a region (if it is active) or the entire buffer, regardless of the position of the cursor.
"Replace current line after keeping or deleting COLUMNS.
Keep the COLUMNS if KEEP? is non-nil, delete otherwise.
Defined columns as the text between SEPARATOR."
(cl-labels ((keep-oper (idx it) (if keep?
(when (member idx columns) it)
(unless (member idx columns) it))))
(let* ((start (line-beginning-position))
(end (line-end-position))
(line (buffer-substring start end))
(parts (thread-last (split-string line separator)
(--map-indexed (keep-oper it-index it))
(-remove 'null)))
(nline (string-join parts separator)))
(delete-region start end)
(insert nline))))
#+end_src
I like the idea of the shell command, =cut=, where you can have an arbitrary character as a separator, and then either delete or keep the data between them, as columns. But I need a function that can convert a string of “columns”, for instance ="1, 4-7 9"= to an list of numbers, like ='(1 4 5 6 7 9)=:
The [[help:sort-fields][sort-fields]] function does a good job if the table is space separated, but if we separate by some other character(s), it doesn’t work. Can we write a function that does this? Here we make a /helper/ to some regular expression for the [[help:sort-regexp-fields][sort-regexp-fields]] function.
#+begin_src emacs-lisp
(defun ha-sort-table-by-column (separator field)
"Sort the active region or entire buffer by column, FIELD.
Columns are denoted by a regular expression, SEPARATOR, which
could be a single character. For instance, given a buffer:
d, a, c, b
x, y
e, f, g
i, m, a, o
Calling this function with a `,' separator, and `2' for the
column, would result in:
d, a, c, b
e, f, g
i, m, a, o
x, y"
(interactive "sSeparator: \nnSorting field: ")
;; Create a regular expression of grouped fields, separated
;; by the separator sequence, for commas, this would be e.g.