How to eliminate # of columns in a pipe delimited file, in vim - vim

I have a pipe-delimited file I am working with in Vim, how can I eliminate columns using this? For example - deleting everything before the third pipe on every line of the file

If your pipe delimited lines start at line #1, move the cursor to line #1,
and press Shift-o (letter 'o') to add a blank line above your first line.
Make sure the cursor is in the new first line. Press
q a
to start to record your key stroke series into register 'a'. (q - start to
record; a - record to register 'a')
Press
j d 3 f | q
(j - down; d - delete; 3 - the 3rd pipe; f - find; | - pipe sign; q -
finish recording)
Press
u
to undo the deletion.
Check how many lines are there all together, say, 1000.
Move the cursor back to the first line, then press
1000 # a
(1000 - run the recorded key stroke series 1000 times; # - run the recorded key
stroke series; a - in register 'a')
You have achieved what you want.

Related

Problem when printing output in Python (with easyinput)

So I'm having trouble with enters and line breaks in my code. I must use easyinput library (and import read).
My code stands for:
Input: Input consists of several cases separated by an empty line. Every case has three parts ('lines'). The first one is a line with the translation table: 26 different characters (with no spaces nor ‘_’), the first one corresponding to ‘a’, the second one to ‘b’, …, and the last one to ‘z’. The second part is a number n > 0 in a line. The third part consists of n encrypted lines of text.
Output: For each case, write the original text, also with n lines. Change each ‘_’ of the encrypted text for a space. Write an empty line at the end of each case.
So I figured out how to solve the problem, the things is that my code prints 'well' entering line by line in input. But the problem input must be entered the whole entire. I put an example for better understanding:
Some input should be:
52-!813467/09*+.[();?`]<:>
6
5_3++!_305))_6*_;48_26)4+.)_4+);80_6*_;48_!8`60)_)85;
;]8*;:_+*8_!83(88)_5*!_;46(;88*_96*?;8)
*+(;485);_5*!_2:_*+(;4
956*_2(5*-4_)8`8*;4_0692_85);_)6!8
)4++;_1(+9_;48_081;_8:8_+1_;48_!85;4)_485!
5_288_06*8_1(+9_;48_;(88_;4(+?34_;48_)4+;_161;:_188;_+?;
bcdefghijklmnopqrstuvwxyza
3
cfxbsf_pg_cvht_jo_uif_bcpwf_dpef
j_ibwf_pomz_qspwfe_ju_dpssfdu
opu_usjfe_ju
And its output must be:
a good glass in the bishops hostel in the devils seat
twenty one degrees and thirteen minutes
northeast and by north
main branch seventh limb east side
shoot from the left eye of the deaths head
a bee line from the tree through the shot fifty feet out
beware of bugs in the above code
i have only proved it correct
not tried it
My code far now is:
from easyinput import read
abc = 'abcdefghijklmnopqrstuvwxyz'
values = [letter for letter in abc]
old_abc = read(str)
while old_abc is not None:
keys = [old_letter for old_letter in old_abc]
dict_abc = dict(zip(keys, values))
num_lines = read(int)
for i in range(num_lines):
line = read(str)
for j in line:
if j == '_':
print(' ', end = '')
else:
print(dict_abc[str(j)], end = '')
print('\n')
old_abc = read(str)
I do not find a way of making my code easier, I just want some help to finally print the desired output. Thanks

How to navigate to indendation levels in vim

I have the following code (line numbers included):
1 def test():
2 a = 1
3 b = 1
4 c = 1
5 d = 1
6 if a == 1:
7 print('This is a sample program.')
And the cursor is on line 7, the last line. Is there a fast, and ideally one key, way to navigate up to line 6, which is one indentation level up, and then, on the next key press, to line 1, one indentation level up again? Conversely, is there a matching method to "drill down" that way?
There is a plugin for that: https://github.com/jeetsukumaran/vim-indentwise
The mappings it provides that match what you are looking for are:
[- : Move to previous line of lesser indent than the current line.
[+ : Move to previous line of greater indent than the current line.
]- : Move to next line of lesser indent than the current line.
]+ : Move to next line of greater indent than the current line.
Then, if you really wanted to do what you asked for in a single keypress, you can remap them like so, for example:
nmap - [-
nmap + ]+

Jump from the beginning of a line to the end of the line above

I want to know what is required in .vimrc to achieve the following.
Consider the following situation:
Line 1 -> ABC DEF GHI
Line 2 -> JKL MNK OPQ
where A and J are both the beginning of each line, and I and Q are the end of those lines, respectively.
Case (1)
Suppose that the cursor is in J. In order to move from J to I, I have to press a key k and a key $ in my current setting. I want to configure MacVim so that pressing a key h brings the cursor to I.
Case (2)
Suppose that the cursor is in I. In order to move from I to J, I have to press a key j and a key 0 in my current setting. I want to configure MacVim so that pressing a key l ("el") brings the cursor to J.
Can anyone help?
case(1): cursor J -> I :
press ge or gE
case(2): cursor I -> J :
press w or W
You're looking for
:set whichwrap+=h,l
(But its help says this setting is not recommended, probably because it's against the original vi behavior and might break some macros and plugins.)

How to efficiently interlace multiple groups of lines in Vim?

I am trying to interlace three groups of lines of text. For example, the following text:
a
a
a
b
b
b
c
c
c
is to be transformed into:
a
b
c
a
b
c
a
b
c
Is there an efficient way of doing this?
Somewhere in the depths of my ~/.vim files I have an :Interleave command (appended below). With out any arguments :Interleave will just interleave just as normal. With 2 arguments how ever it will specify how many are to be grouped together. e.g. :Interleave 2 1 will take 2 rows from the top and then interleave with 1 row from the bottom.
Now to solve your problem
:1,/c/-1Interleave
:Interleave 2 1
1,/c/-1 range starting with the first row and ending 1 row above the first line matching a letter c.
:1,/c/-1Interleave basically interleave the groups of a's and b's
:Interleave 2 1 the range is the entire file this time.
:Interleave 2 1 interleave the group of mixed a's and b's with the group of cs. With a mixing ratio of 2 to 1.
The :Interleave code is below.
command! -bar -nargs=* -range=% Interleave :<line1>,<line2>call Interleave(<f-args>)
fun! Interleave(...) range
if a:0 == 0
let x = 1
let y = 1
elseif a:0 == 1
let x = a:1
let y = a:1
elseif a:0 == 2
let x = a:1
let y = a:2
elseif a:0 > 2
echohl WarningMsg
echo "Argument Error: can have at most 2 arguments"
echohl None
return
endif
let i = a:firstline + x - 1
let total = a:lastline - a:firstline + 1
let j = total / (x + y) * x + a:firstline
while j < a:lastline
let range = y > 1 ? j . ',' . (j+y) : j
silent exe range . 'move ' . i
let i += y + x
let j += y
endwhile
endfun
Here is a "oneliner" (almost), but you have to redo it for every unique line minus 1, in your example 2 times. Perhaps of no use, but I think it was a good exercise to learn more about patterns in VIM. It handles all kind of lines as long as the whole line is unique (e.g. mno and mnp are two unique lines).
First make sure of this (and do not have / mapped to anything, or anything else in the line):
:set nowrapscan
Then map e.g. these (should be recursive, not nnoremap):
<C-R> and <CR> should be typed literally.
\v in patterns means "very magic", #! negative look-ahead. \2 use what's found in second parenthesis.
:nmap ,. "xy$/\v^<C-R>x$<CR>:/\v^(<C-R>x)#!(.*)$\n(\2)$/m-<CR>j,.
:nmap ,, gg,.
Then do ,, as many times as it takes, in your example 2 times. One for all bs and one for all cs.
EDIT: explanation of the mapping. I will use the example in the question as if it has run one time with this mapping.
After one run:
1. a
2. b
3. a
4. b
5. a
6. b
7. c
8. c
9. c
The cursor is then at the last a (line 5), when typing ,,, it first go back to first line, and then runs mapping for ,., and that mapping is doing this:
"xy$ # yanks current line (line 1) to reg. "x" ("a") "
/\v^<C-R>x$<CR> # finds next line matching reg. "x" ("a" at line 3)
:/\v^(<C-R>x)#!(.*)$\n(\2)$/m-<CR>
# finds next line that have a copy under it ("c" in line 7) and moves that line
# to current line (to line 3, if no "-" #after "m" it's pasted after current line)
# Parts in the pattern:
- ^(<C-R>x)#!(.*)$ # matches next line that don't start with what's in reg. "x"
- \n(\2)$ # ...and followed by newline and same line again ("c\nc")
- m-<CR> # inserts found line at current line (line 3)
j # down one line (to line 4, where second "a" now is)
,. # does all again (recursive), this time finding "c" in line 8
...
,. # gives error since there are no more repeated lines,
# and the "looping" breaks.
I just ran into this issue independently tonight. Mine's not as elegant as some of the answers, but it's easier to understand I think. It makes many assumptions, so it's a bit of a hack:
A) It assumes there's some unique character (or arbitrary character
string) not present in any of the lines - I assume # below.
B) It
assumes you don't want leading or trailing white space in any of the
a, b, or c sections.
C) It assumes you can easily identify the
maximum line length, and then pad all lines to be that length (e.g.
perhaps using %! into awk or etc., using printf)
Pad all lines with spaces to the same maximum length.
Visual Select just the a and b sections, then %s/$/#
Block copy and past the b section to precede the c section.
Block copy and paste the a section to precede the bc section.
%s/#/\r
%s/^ *//g
%s/ *$//g
delete the lines left where the a and b sections were.
If you have xclip you can cut the lines and use paste to interleave them:
Visual select one set of lines
Type "+d to cut them to the clipboard
Visual select the other set of lines
Type !paste -d '\n' /dev/stdin <(xclip -o -selection clipboard)
Put the following as interleave.awk in your path, make it executable.
#!/usr/bin/awk -f
BEGIN { C = 2; if (ARGC > 1) C = ARGV[1]; ARGV[1]="" }
{ g = (NR - 1) % C; if (!g) print $0; else O[g] = O[g] $0 "\n" }
END { for (i = 1; i < C; i++) printf O[i] }
Then from vim highlight the lines in visual mode, then call :'<,'>!interleave.awk 3, or replace 3 with however many groups to interleave (or leave blank for 2).
You asked for an efficient way. Interpreted languages aside, this may be the most efficient algorithm for interleaving arbitrary lines - the first group are immediately printed, saving some RAM. If RAM was at a premium (eg, massive lines or too many of them) you could instead store offsets to the start of each line, and if the lines had a consistent well defined length (at least within groups), you wouldn't even need to store offsets. However, this way the file is scanned only once (permitting use of stdin), and CPUs are fast at copying blocks of data, while file pointer operations probably each require a context switch as they would normally have to trigger a system call.
Perhaps most importantly, the code is simple and short - and efficiency of reading and implementation are usually the most important of all.
Edit: looks like others have come to the same solution - just found https://stackoverflow.com/a/16088069/118153 when reframing the question in a search engine to see if I'd missed something obvious.

AWK reporting duplicate lines and count, program explanation

I found the following AWK program on the internet and tweaked it slightly to look at column $2:
{ a[$2,NR]=$0; c[$2]++ }
END {
for( k in a ) {
split(k,b,SUBSEP)
t=c[b[1]] # added this bit to capture count
if( b[1] in c && t>1 ) { # added && t>1 only print if count more than 1
print RS "TIMES ID" RS c[b[1]] " " b[1] RS
delete c[b[1]]
}
for(i=1;i<=NR;i++) if( a[b[1],i] ) {
if(t>1){print a[b[1],i]} # added if(t>1) only print lines if count more than 1
delete a[b[1],i]
}
}
}
Given the following file:
abc,2,3
def,3,4
ghi,2,3
jkl,5,9
mno,3,2
The output is as follows when the command is run:
Command: awk -F, -f find_duplicates.awk duplicates
Output:
TIMES ID
2 2
abc,2,3
ghi,2,3
TIMES ID
2 3
def,3,4
mno,3,2
This is fine.
I would like to understand what is happening in the AWK program.
I understand that the first line is loading each line into a multidimentional array ?
So first line of file would be a['2','1']='abc,2,3' and so on.
However I'm a bit confised as to what c[$2]++ does, and also what is the significance of split(k,b,SUBSEP) ??
Would appreciate it if someone could explain line by line what is going on in this AWK program.
Thanks.
The increment operator simply adds one to the value of the referenced variable. So c[$2]++ takes the value for c[$2] and adds one to it. If $2 is a and c["a"] was 3 before, its value will be 4 after this. So c keeps track of how many of each $2 value you have seen.
for (k in a) loops over the keys of a. If the value of $2 on the first line was "a", the first value of k will be "a","1" (with 1 being the line number). The next time, it will be the combination of the value of $2 from the second line and the line number 2, etc.
The split(k,b,SUBSEP) will create a new array b from the compound value in k, i.e. basically reconstruct the parts of the compound key that went into a. The value in b[1] will now be the value which was in $2 when the corresponding value in a was created, and the value in b[2] will be the corresponding line number.
The final loop is somewhat inefficient; it loops over all possible line numbers, then skips immediately to the next one if an entry for that ID and line number did not exist. Because this runs inside the outer loop for (k in a) it will be repeated a large number of times if you have a large number of inputs (it will loop over all input line numbers for each input line). It would be more efficient, at the expense of some additional memory, to just build a final output incrementally, then print it all after you have looped over all of a, by which time you have processed all input lines anyway. Perhaps something like this:
END {
for (k in a) {
split (k,b,SUBSEP)
if (c[b[1]] > 1) {
if (! o[b[1]]) o[b[1]] = c[b[1]] " " b[1] RS
o[b[1]] = o[b[1]] RS a[k]
}
delete a[k]
}
for (q in o) print o[q] RS
}
Update: Removed the premature deletion of c[b[1]].

Resources