Search code examples
vim

Transpose these two colums into rows?


I've copy pasted some data from a webpage:

x       y
0       9
2       22.6
4       44.2
5       62.1
7       96.9
8       113.4
10      133.4
11      137.6
15      148.4
17      149.3

I'd like to transpose and slightly transform the data such:

x = 0,2,4,...
y = 9, 22.6, 44.2...

I know how to put commas after each column using v mode but I don't know how that could work for when I have double digits or a column with mixed length of characters.

I'm more concerned about how to transpose, if that is the command that I want?

How can I get my data into the desired form:

x = 0,2,4,...
y = 9, 22.6, 44.2...

Solution

  • If you have the external tool rs installed see second approach below, otherwise:

    Pure Vim

    My approach would centre around using visual block mode with Ctrl + v and applying the J command, and cleaning up with a substitution. First select the second column in visual block and delete it, then paste it below the x column:

    x   
    0   
    2   
    4   
    5   
    7   
    8   
    10  
    11  
    15  
    17  
    
    y
    9
    22.6
    44.2
    62.1
    96.9
    113.4
    133.4
    137.6
    148.4
    149.3
    

    Then you can do vipJ, on each block, to select that data and put it on one line (FYI: vipJ is going to visual mode, selecting inside paragraph, and then Joining those lines):

    x   0   2   4   5   7   8   10  11  15  17  
    
    y 9 22.6 44.2 62.1 96.9 113.4 133.4 137.6 148.4 149.3
    

    A quick substitution :s/\s\+/, /g can replace spaces with commas:

    , x, 0, 2, 4, 5, 7, 8, 10, 11, 15, 17, 
    
    , y, 9, 22.6, 44.2, 62.1, 96.9, 113.4, 133.4, 137.6, 148.4, 149.3
    

    Then you can just clean up the extra commas, and add the = sign, remove the blank line etc:

    x = 0, 2, 4, 5, 7, 8, 10, 11, 15, 17
    y = 9, 22.6, 44.2, 62.1, 96.9, 113.4, 133.4, 137.6, 148.4, 149.3
    

    If there are many columns, you probably don't want to repeat the procedure above for each one... in that case you can probably record the above as a macro in a repeatable way or check out the approach below.

    Using external tool rs

    The rs tool allows you to reshape a data array, so you could filter your file (or a visual selection, or passing a range) through the tool from inside vim like so:

    :%!rs -T -C,
    

    The flags -T means to transpose, and -C specifies the column delimiter in the output (a comma in our case) to produce:

    x,0,2,4,5,7,8,10,11,15,17,
    y,9,22.6,44.2,62.1,96.9,113.4,133.4,137.6,148.4,149.3,
    

    Then as in the pure vim approach, you just need to do some clean up:

    :%s/,/, /g
    

    produces:

    x, 0, 2, 4, 5, 7, 8, 10, 11, 15, 17, 
    y, 9, 22.6, 44.2, 62.1, 96.9, 113.4, 133.4, 137.6, 148.4, 149.3, 
    

    Then you can just clean up the extra commas and add the = sign:

    x = 0, 2, 4, 5, 7, 8, 10, 11, 15, 17
    y = 9, 22.6, 44.2, 62.1, 96.9, 113.4, 133.4, 137.6, 148.4, 149.3
    

    Bonus

    If needed, you could run the external command (also on a visual selection, or passing a range) :%!column -t to align things nicely:

    x  =  0,  2,     4,     5,     7,     8,      10,     11,     15,     17
    y  =  9,  22.6,  44.2,  62.1,  96.9,  113.4,  133.4,  137.6,  148.4,  149.3