4

I come about this regularly and still do it manually. Consider this example - say it's inside a text.

 first 3,2,17,198,232,1,999 and some other text
 second 1,2,3,71,98,4,5 and some more text

I need these values sorted (usually for easier read, or to find duplicates).

e.g.

 first 1,2,3,17,198,232,999 and some other text
 second 1,2,3,4,5,71,98 and some more text

So far I always do it the "pedestrian" way - in my head where possible, or yank out sort there and paste back. I keep thinking there should be a more efficient way in vim. (I also tried splitting those into lines, sort -n, join, adjust commas,... - but this was even less efficient.)

Preferable without needing me to install a plugin as I sometimes work on other machines.

2 Answers 2

4

Vanilla Vim: Via Expression Register

And now a solution for vanilla vim:

  1. Position cursor on list
  2. ciW (removes unsorted list and puts it into register ")
  3. <C-r>=join(sort([<C-r>"]), ',')<CR>

Done. I think it is actually not that bad. An even more useful variant would be

  1. <C-r>=join(sort(split(expand(@"), ',')), ',')<CR>

You can re-use this expression the next time you want to sort a different list via the expression history (:h history):

  1. Position cursor on the different list
  2. ciW

Cycle through the history with the arrow keys to find the expression:

  1. <C-r>=<Up><Up>...<CR> or
    <C-r>=join<Up><CR>

There is an additional convenience feature at the expression prompt:

If you do not enter an expression, Vim uses the previous expression (like with the "/" command)

  1. <C-r>=<CR>

If you use the expression register quite often and you want to avoid losing older expressions, you should make sure that the value of 'history' is large enough: :set history?. If you have not set this value by yourself (vanilla vim), the current setting could be one of
vi-'compatible': 0, vim default: 50, defaults.vim since vim8: 200, vim-sensible: 1000 (e.g. arch)

Alternatively, define a short vimscript function to avoid typing the 'advanced' command at the expression register prompt:

function! Mysort()
   return join(sort(split(@", ',')), ',')
endfunction
  1. <C-r>=Mysort()<CR> (tab-completion might help to ease the typing)

IMHO this vanilla vim solution would be almost as good as the plugin if you could dot-repeat step 2 and 3. Afaik, this is not possible. You could record steps two and three to a macro (e.g. qq) and then replay it (with @q (1st replay), @@ (2nd and more replays)).

7
  • Very nice! At the moment dot-repeatability is not an issue so I prefer something that in case of emergency I can type from my head.
    – bdecaf
    Commented Oct 1, 2018 at 7:08
  • You are welcome. I had the impression that you have to do this quite often and then dot-repeatability makes vim really shine. If you only have to do this once or twice per document, this is certainly less impressive and I would argue that my solution takes more effort than ordering 7 numbers manually. Also if there is only a second list, I would still denote it as premature workflow optimization.
    – Hotschke
    Commented Oct 1, 2018 at 7:26
  • Actually my main goal is to eliminate human error. I usually have just one or two such lists in a file - but these change frequently - often just a few numbers are added. Repeatedly doing this boring repetitive sorting manually I just happen to make hard to spot errors.
    – bdecaf
    Commented Oct 2, 2018 at 8:15
  • I see. Also when the list is larger, say 20 numbers, it is a hassle to sort it manually, even a single time. Just out of curiosity: denote these numbers references in a bibliography?
    – Hotschke
    Commented Oct 2, 2018 at 8:21
  • 1
    I was surprised that csv cli tools such as csvkit, csvtk, miller, xsv and awk cannot easily sort a single line/record. Therefore, I was wondering whether there is an alternative for storing the data which would make your life easier. But I guess you are happy with it as it is. BTW a new tool I stumbled upon on github.com/dbohdan/structured-text-tools is pawk (python as awk): echo '3,2,17,198,232,1,999' | pawk -F ',' "','.join(sorted(f,key=int))".
    – Hotschke
    Commented Oct 2, 2018 at 9:50
3

Plugin sort-motion.vim

IMO the most convenient method to achieve this is to use a plugin or a self-written vim function linked to a mapping. Both need a customized vim and are obviously not available in vanilla vim.

For example, the plugin sort-motion.vim provides a flexible, very convenient solution for your question.

  1. Position the cursor anywhere on the comma-separated list
  2. gsiW

This is dot-repeatable:

  1. Position on next list which you want to sort
  2. .

IMHO minimal mental effort required which means less "mental energy" depletion. If your list exceeds one line, I would recommend to join them, sort and then re-flow the paragraph.

I am pretty confident that there is no shorter vimgolf solution for this.

Also tools from the command line do not provide this level of convenience (e.g. tr , "\n" | sort | tr "\n" , from Unix & Linux: Sort comma-separated fields on each line by numeric value).

Related command line solutions can be found here

csvkit with csvsort and csvtool transpose offer no easy solution as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.