1

Is there a way I can perform a batch-regex-replace using a list of words?

Simplified example:

List of words

Apple  
Cabbage  
Turtle

Text to search

Bike 2  
Turtle 5  
Bike 3  
Bike 5  
Cabbage 2  
Bike 0  
Bike 0  
Apple 1  

Output

Turtle = 5;  
Cabbage = 2;  
Apple = 1;  

What I'd like to do is something like:

:%s/\\(\list\\).*\\(\d\\)/\1 = \2;/

Note that the actual list I'm using is quite long (~50 items).

1
  • If you want to do in bulk, you may save those command as auto commands in vimrc file. Whenever you open any file it with replace all those combinations in the file.
    – SibiCoder
    Oct 20, 2016 at 6:10

4 Answers 4

1

You can make a list in regular expressions using the or operator: \| So yours would look something like:

:%s/\(Apple\|Cabbage\|Turtle\)\s\+\(\d\+\)/\1 = \2;/

Then if you wanted to delete the rest of the lines that didn't match you could use a global command:

:g!/Apple\|Cabbage\|Turtle/ d

See :h /bar and :h :global for more info.

3
  • I guess I could insert all 50 items into the command. But ideally I'd like an easier solution.
    – Ralph
    Oct 18, 2016 at 20:27
  • How are you storing the list? Like an actual vimscrip list variable? You never specified.
    – Tumbler41
    Oct 18, 2016 at 21:34
  • @nateslager I'm not sure how much easier you can expect it to be for what I assume is a one-time thing. Besides being a text editor, Vim is an extreme text manipulation tool. ggVGJ:s/\s\+/\\|/g<cr>Y and now you have your pattern to paste into the command window when you type /<c-f>. You could say that's not easy enough, but x(50+y) keystrokes to do the same thing line-by-line is your alternative.
    – Tommy A
    Oct 19, 2016 at 5:16
1

Assuming your list of words is in a buffer looking like this (one word per line. "Words" with spaces in them are fine.):

Apple  
Cabbage  
Turtle
  1. Append \| to each line:

    g/./normal A\|
    
  2. That should leave the cursor on the last character of the last line. Delete the \| on the last line:

    hd$
    
  3. Merge all the lines into one (if you have more than 50 lines, you may need a larger number. It shouldn't hurt to use a larger number than necessary, so feel free to overestimate):

    gg50gJ
    
  4. At this point, your buffer's contents should look an awful lot like a search pattern:

    apple\|banana\|cabbage
    

    Copy that line into a register (replace the a with your preferred register):

    0"ay$
    
  5. Follow Tumbler41'a instructions. When entering the search pattern, use the register's contents with Ctrl-r a (a being which ever register you used).

1

So let's see how over-engineered we can get:

Let's say that an easier way to communicate to vim the words to process is to get them in a vimscript list. We can then simply concatenate the list with the \| operator to create the desired pattern and apply the substitution that @Tumbler41 suggested in his answer:

function! GetWords()
    " List the items to process
    let list = ['Apple', 'Cabbage', 'Turtle']

    " Concatenate the list to make a pattern
    let pattern = list[0]
    for index in range(1, len(list)-1)
        let pattern = pattern . '\|' . list[index]
    endfor

    " Ashamedly stealing @Tumbler41 solution
    execute '%s/\('.pattern.'\)\s\+\(\d\+\)/\1 = \2;/'
    execute 'v/'.pattern.'/d'
endfunction

This way you can simply open the buffer containing the text to change and use :call GetWords().


Cool, but can we make it even more over-complicated? Sure!

Lets say that the ultimate easiest way to get the list of words to process is to simply write them in a buffer.

You'll have 2 buffer, the one you showed in the question and a second one containing

Apple
Cabbage
Turtle

With this function:

function! GetWords(bufList, bufModify)
    " Go to the buffer containing the list
    execute 'b' . a:bufList

    " List the items to process
    let list = getline(1, '$')

    " Concatenate the list to make a pattern
    let pattern = list[0]
    for index in range(1, len(list)-1)
        let pattern = pattern . '\|' . list[index]
    endfor

    " Go to the buffer to modify
    execute 'b' . a:bufModify

    " Ashamedly stealing @Tumbler41 solution
    execute '%s/\('.pattern.'\)\s\+\(\d\+\)/\1 = \2;/'
    execute 'v/'.pattern.'/d'
endfunction

You can simply use :call GetWords(3, 4) where 3 and 4 are the number of the buffers containing the list and containing the text to modify and the function will do the transformation.


Credits goes to @Tumbler41 for the substitution command :-)

0

Like the other answers, I'm assuming the list is in a different buffer, one entry per line. This one is similar to @9bittree's solution, but it uses my UnconditionalPaste plugin to quickly build the substitution command.

  1. Yank the list of words, e.g. ggyG or :%yank
  2. Build the substitution command: :%s/\(
  3. Now, insert the yanked words, joined with a \| pattern separator. With my plugin, that's <C-R><C-Q>"\\|<Enter>
  4. Complete the substitution command, and execute: \)\s\+\(\d\+\)/\1 = \2;/<Enter>

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.