In this post, I want to share how to do number arithmetic quickly and get what you want in specified format. More specifically, the topic is:

How do we add, subtract, multiply or divide a value to each number in a range?

Suppose that we have the following numbers at the end of each line:

line 1: 10
line 2: 20
line 3: 30
line 4: 40
line 5: 50

How to we do arithmetic operation to those numbers at the end of each line quickly?

We can use expression in substitution command to do this task, for details, see :h sub-replace-expression inside Vim. The idea is that instead of substitute what you search with a string, we will evaluate an expression and use the evaluated result as the substitution string. The general syntax for substitution is:

:s/PATTERN/REPLACE/[flag]

To use an expression in REPLACE string, you can start the replacement string with \=, and the following string will be interpreted as an expression by Vim. If you have used capture groups in PATTERN, you can retrieve the captured text in the corresponding capture group with submatch() function. For example, submatch(1) will return the text in the 1st capture group.

With all this knowledge, we can easily solve the above problem. For example, to add 2 to the number at the end of each line, do:

:1,5s/\v(\d+)$/\=submatch(1)+2

Similarly, to minus or multiply by 2:

:1,5s/\v(\d+)$/\=submatch(1)-2
:1,5s/\v(\d+)$/\=submatch(1)*2

For division, things are a little different here, because the division character / is the same as the substitute delimiter. In order to use division character in substitute expression, we can change the delimiter to other characters, for example, to #:

:1,5s#\v(\d+)$#\=submatch(1)/2.0

Note that for division, we need to divide those numbers by 2.0, not by 2. If you use two integers, Vim will use integer division, not float division1.

One minor issue with the division result is that the result may have many decimal digits. You may want to round it to fixed digit, e.g., 3 digit. This can be easily accomplished with the printf() function, and the whole command looks like:

:1,5s#\v(\d+)$#\=printf('%.3f', submatch(0)/2.3)

References


  1. For example, if we do :echo 5/2, we will get 2 instead of 2.5. ↩︎