2

How can I sort by frequency without deleting any?

one
two
one

to

one
one
two

The output of the standard sort-by-frequency :%!sort | uniq -c | sort -nr is

   2 one
   1 two

It has deleted all the copies of the line (bc of uniq), and the spaces and numbers are not desired in the output.

So how can I sort by frequency and keep all lines in VIM?

4

2 Answers 2

1

The output of your current pipeline is always in the format

[zero or more spaces][number of repetitions][space][actual line contents]
                     ^^^^^^^^^^^^^^^^^^^^^^^       ^^^^^^^^^^^^^^^^^^^^^^
                         First field: $1         Remaining fields: $2, $3...

We can thus set up a for-loop that prints the actual line contents $1 times.

If the file is as simple as you make it look (each line only contains one word), use

:%!sort | uniq -c | sort -nr | awk '{for(i=$1;i>0;i--)print $2}'

Otherwise (this is the general answer), use

:%!sort | uniq -c | sort -nr | awk '{n=$1;sub(/ *[0-9]+ +/,"");for(i=n;i>0;i--)print}'
0
1

Here's a mostly vimscript way. Start with your pipeline:

:%!sort | uniq -c | sort -nr

Now, either

  • use a :global to delete the number and the line, then repeatedly paste
:global/./execute 'normal! 0"adawdd' | let @a = str2nr(@a) | execute 'normal!' @a.'P'
  • somewhat shorter, but may break depending on the number format (e.g., 010 becomes 8)
:global/./execute 'normal! ^"adiwdd' | execute 'normal!' @a.'P'
  • use a substitute with \= to compute the replacement
:%substitute/^\s*\(\d\) \(.*\)/\=repeat([submatch(2)], submatch(1)->str2nr())->join("\n")

Your Answer

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

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