# Emacs align matrices

Since I find myself writing a lot of matrices and tables, I'm looking for a way to align numbers nicely in Emacs (similar to the align package in vim). I found that there is align-regexp, but I couldn't get it to work the way I wanted. Is there a way to align numbers at their decimals --- and if there are no decimals align in front of the other decimals. It would also be nice to be able to align at 'thousands'-separators and align complex numbers. Preferably with two whitespaces in-between numbers for readability. Here is an example:

Input:

``````A = [-15 9 33.34;...
1.0 0.99 1+3i;...
13,000 2 11 ];
``````

Desired output:

``````A = [   -15     9     33.34 ;...
1.0  -0.99   1+3i ;...
13,000     2     11    ];
``````

Alternatively, to make it a bit easier (without 'thousands'-separator and complex numbers):

Input:

``````A = [-15 9 33.34;...
1.0 0.99 1;...
13000 2 11 ];
``````

Desired output:

``````A = [  -15     9      33.34 ; ...
1.0   0.99    1    ; ...
13000     2      11    ];
``````

Thanks a lot.

This took me quite a bit more time than I originally estimated, and the code is a bit too long to post it all here, so I posted it to Patebin: http://pastebin.com/Cw82x11i

It is not entirely complete though and it can use some more work, so if anyone will have suggestions or contributions, I might re-arrange this as a Git repository somewhere / repost this to Emacs wiki.

Few important points:

1. No attempt has been made to cater for matrices with delimiters other than spaces.
2. I didn't try to parse complex numbers either.
3. The treatment of non-numerical entries is different than the one in your example (to be honest, I wouldn't really know how to parse it in exactly the way you want. My guess is that the semicolon is the Matlab / Octave row delimiter, but if I try to make it more generic, it's really hard to wrap my head around it. Also, my guess is that the ellipsis is the Matlab / Octave way of telling the interpreter that the statement continues on the next line, but, again, trying to make this more generic would be really hard. Instead, I'm just treating whatever non-numeric value I encounter as if it was a whole number.
4. Finally, I had to give up the `align-regexp` because it was too complicated to try to make it align exactly using the rule you seem to have in mind.

Here's what it would look like:

``````;; before
A = [-15 9 33.34;...
1.0 0.99 1;...
13000 2 11 ];

;; after
A = [  -15   9    33.34 ;...
1.0 0.99  1    ;...
13000   2    11         ];
``````

PS. You can adjust the space between the columns by changing the value of the `spacer` variable.

OK, I've also made a little refinement to the code where it can now ask for the string to fill in between the columns.

``````(defun my/string-to-number (line re)
(let ((matched (string-match re line)))
(if matched
(list (match-string 0 line)
(substring line (length (match-string 0 line))))
(list nil line))))

(defun my/string-to-double (line)
(my/string-to-number
line
"\\s-*[+-]?[0-9]+\\(?:\\.[0-9]+\\(?:[eE][+-]?[0-9]+\\)?\\)?"))

(defun my/string-to-int (line)
(my/string-to-number line "\\s-*[+-]?[0-9]+"))

(defun my/vector-transpose (vec)
(cl-coerce
(cl-loop for i below (length (aref vec 0))
collect (cl-coerce
(cl-loop for j below (length vec)
collect (aref (aref vec j) i))
'vector))
'vector))

(defun my/align-metric (col num-parser)
(cl-loop with max-left = 0
with max-right = 0
with decimal = 0
for cell across col
for nump = (car (funcall num-parser cell))
for has-decimals = (cl-position ?\. cell) do
(if nump
(if has-decimals
(progn
(setf decimal 1)
(when (> has-decimals max-left)
(setf max-left has-decimals))
(when (> (1- (- (length cell) has-decimals))
max-right)
(setf max-right (1- (- (length cell) has-decimals)))))
(when (> (length cell) max-left)
(setf max-left (length cell))))
(when (> (length cell) max-left)
(setf max-left (length cell))))
finally (cl-return (list max-left decimal max-right))))

(defun my/print-matrix (rows metrics num-parser prefix spacer)
(cl-loop with first-line = t
for i upfrom 0
for row across rows do
(unless first-line (insert prefix))
(setf first-line nil)
(cl-loop with first-row = t
for cell across row
for metric in metrics
for has-decimals =
(and (cl-position ?\. cell)
(car (funcall num-parser cell)))
do
(unless first-row (insert spacer))
(setf first-row nil)
(cl-destructuring-bind (left decimal right) metric
(if has-decimals
(cl-destructuring-bind (whole fraction)
(split-string cell "\\.")
(insert (make-string (- left (length whole)) ?\ )
whole
"."
fraction
(make-string (- right (length fraction)) ?\ )))
(insert (make-string (- left (length cell)) ?\ )
cell
(make-string (1+ right) ?\ )))))
(unless (= i (1- (length rows)))
(insert "\n"))))

(cl-coerce
(cl-loop for line in (split-string
(buffer-substring-no-properties beg end) "\n")
collect
(cl-coerce
(nreverse
(cl-loop with result = nil
with remaining = line do
(cl-destructuring-bind (num remainder)
(funcall num-parser remaining)
(if num
(progn
(push (org-trim num) result)
(setf remaining remainder))
(push (org-trim remaining) result)
(cl-return result)))))
'vector))
'vector))

(defvar my/parsers '((:double . my/string-to-double)
(:int . my/string-to-int)))

(defun my/align-matrix (parser &optional spacer)
(interactive
(let ((sym (intern
"Parse numbers using: "
(mapcar 'car my/parsers)
nil nil nil t ":double")))
(spacer (if current-prefix-arg
" ")))
(list sym spacer)))
(unless spacer (setf spacer " "))
(let ((num-parser
(or (cdr (assoc parser my/parsers))
(and (functionp parser) parser)
'my/string-to-double))
beg end)
(if (region-active-p)
(setf beg (region-beginning)
end (region-end))
(setf end (1- (search-forward-regexp "\\s)" nil t))
beg (1+ (progn (backward-sexp) (point)))))
(goto-char beg)
(let* ((prefix (make-string (current-column) ?\ ))
• @DayAndNight this is really strange. I can't find a place where this error may happen. But if you can give me an example data, then my chances will be better. You may not need to mark the region before calling `my/align-matrix`. If the numbers are inside something Emacs treats as a kind of parenthesis (typically anyone of [], (), {}), then the code will make an effort to find that region on its own. Aug 26, 2015 at 7:44