How to transpose the contents of lines and columns in a file in Vim?
Asked Answered
O

6

14

I know I can use Awk, but I am on a Windows box, and I am making a function for others that may not have Awk. I also know I can write a C program, but I would love not to have something that requires compilation and maintenance for a little Vim utility I am making.

The original file might be:

THE DAY WAS LONG 
THE WAY WAS FAST

and after the transposition, it should become:

TT
HH
EE

DW
AA
YY

WW
AA
SS

LF
OA
NS
GT

Update

Olecranon answered 1/4, 2009 at 5:9 Comment(3)
Lowest number of characters... lowest score wins kind of thing.Olecranon
It would have been even better if the solution transposed only the selected text... But, okay, the current solution is good enough for me. :)Valvule
The script in my answer below seems to be the shortest Vim-only solution in the “golf” sense—that you have declared as the deciding factor for selecting between correct answers—among those posted so far, is it not?Conformity
C
12

Here is a command in Vim language. So you don't have to compile Vim with +python support.

function! s:transpose()
    let maxcol = 0
    let lines = getline(1, line('$'))

    for line in lines
        let len = len(line)
        if len > maxcol 
            let maxcol = len
        endif
    endfor

    let newlines = []
    for col in range(0, maxcol - 1)
        let newline = ''
        for line in lines
            let line_with_extra_spaces = printf('%-'.maxcol.'s', line)
            let newline .= line_with_extra_spaces[col]
        endfor
        call add(newlines, newline)
    endfor

    1,$"_d
    call setline(1, newlines)
endfunction

command! TransposeBuffer call s:transpose()

Put this in newly created .vim file inside vim/plugin dir or put this to your [._]vimrc.
Execute :TransposeBuffer to transpose current buffer

Consolation answered 1/4, 2009 at 9:42 Comment(4)
Pure vim gets you the nod. Thank you so much!Olecranon
An unnecessarily wordy solution, in my opinion. By the way, technically speaking your solution does not work, since 1,$"_d does not do what you probably want it to do. I would write that line as sil %d_.Conformity
@ib why you say it doesn't? It works for me and it delete the whole file into anonymous register, does it not?Consolation
No, it does not: Open a nonempty buffer and run the Ex command you propose: :1,$"_d—nothing will be deleted. You are trying to use the Ex command :delete the same way as the Normal mode command d. However, both syntax and semantics of those commands are different! The :delete command accepts an optional range before the command name, and an optional register name after (without "). What you have written is the range 1,$ followed by a the commented text _d (see :quote). Thus, the only effect your command has is moving the cursor to the last line of the current buffer.Conformity
O
10

Vim support for a number of scripting languages built in -- see the Python interface as an example.

Just modify vim.current.buffer appropriately and you're set.

To be a little more specific:

function! Rotate()
python <<EOF
import vim, itertools
max_len = max((len(n) for n in vim.current.buffer))

vim.current.buffer[:] = [
    ''.join(n) for n in itertools.izip(
        *( n + ' ' * (max_len - len(n))
           for n in vim.current.buffer))]
EOF
endfunction
Offshore answered 1/4, 2009 at 5:13 Comment(6)
++ oooh - was unaware that vim supported more than vimscript! Also perl, tcl, and ruby -- awesome!Gley
It supports those scripts but not by default. You have to turn them on during build.Consolation
Right. Just about every distribution has a vim-full or vim-enhanced version, and I tested what I posted here against the Windows gvimOffshore
Crazy Kudos I am going to wait to see if any golf answer like four key strokes and your done win out. I wonder what assumptions I should be allowed to make about the vim bulid itself to still be considered portable.Olecranon
You still have to have python2X.dll somewhere on your computer for this to work. Python itself is not embedded in Vim.Siphon
@CharlesDuffy: Just to simplify your code, you can use max(map(len, vim.current.buffer)) instead of max((len(n) for n in vim.current.buffer)), I believe.Damson
I
5

If scripts don't do it for you, you could record the actions to a register (the carriage returns are added for readability):

qa
1G0
xGo<Esc>p
1G0j
xGp
q

This will give you a macro that you could run against the example above, or any 2-line strings of the same length. You only need to know the length of the string so you can iterate the operation the correct number of time

16@a

A fairly basic solution, but it works.

Illegitimate answered 1/4, 2009 at 10:1 Comment(1)
The input domain could be more than two lines with the same length.Olecranon
C
1

The following function performs the required editing operations to “transpose“ the contents of the current buffer, regardless of the number of lines in it.

fu!T()
let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]
g/^/let m=max([m,col('$')])
exe'%norm!'.m."A \e".m.'|D'
sil1norm!@s
exe'%norm!'.n.'gJ'
endf

For the sake of “golfing”, here is its one-line version:

let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]|exe'g/^/let m=max([m,col("$")])'|exe'%norm!'.m."A \e".m.'|D'|exe'sil1norm!@s'|exe'%norm!'.n.'gJ'
Conformity answered 6/9, 2011 at 13:23 Comment(0)
I
0

Charles Duffy's code could be shortened/improved using izip_longest instead of izip:

function! Rotate()
    :py import vim, itertools
    :py vim.current.buffer[:] = [''.join(c) for c in itertools.izip_longest(*vim.current.buffer, fillvalue=" ")]
endfunction
Infuriate answered 6/9, 2011 at 9:58 Comment(0)
L
0

I've developed a vim plugin to do it. You can find it here. Run :Transpose to transpose the whole file.

Landscape answered 6/5, 2012 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.