# Sort by frequency without deleting lines?

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?

• Is there a reason, you cannot use `:sort` command? Dec 19, 2021 at 17:44
• @ChristianBrabandt no Dec 19, 2021 at 19:03
• vi.stackexchange.com/q/21130/10604 Dec 19, 2021 at 19:16

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}'
``````

``````:%!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")
``````