# Finding Numbers in Brackets Followed by a Letter and Summing Them

How can I make a viml function that scans highlighted text for all occurrences of `[ <number> u ]` and `[ <number>u ]` and sums them?

EXAMPLE:

The text

``````Here is some text. [0.1u]
Here is some more text [0.2 u]
And here is some noise: 0.2
``````

should yield `0.3`.

The following solution works for me:

``````vnoremap <silent><expr> <c-x> YankAndAnalyse()
nmap     <silent>       <c-x> vip<c-x><esc>

function! YankAndAnalyse()
return 'y'
\ . ":call Analyse()\<CR>"
\ . ":silent normal! gv\<CR>"
endfunction

function! Analyse()
let selection = split(getreg(''), '\n')
let numbers = map(selection,
\ 'matchstr(v:val, ''\[\s*\zs\d\+\%(\.\d\+\)\?\ze\s*u\s*]'')')
call filter(numbers, '!empty(v:val)')
call map(numbers, 'str2float(v:val)')
if empty(numbers) | return | endif

echohl Identifier
echon 'Sum: ' . printf('%g', eval(join(numbers, ' + ')))
echohl None
endfunction
``````

Here `YankAndAnalyse` is a simple wrapper function that first fills the default register. `Analyse` reads the default register and parses the numbers that match your critera. The `YankAndAnalyse` function is mapped to `CTRL-x` in visual mode (feel free to choose whatever you want), and the normal mode map simply applies the visual mode map to the current paragraph.

See the documentation for more help, e.g. `:h matchstr()` for help on the `matchstr` function.

Update: The above solution only sums the first matched number in each line. In order to sum all matched numbers, one may use the following version of the `Analyse` function:

``````function! Analyse()
let input = getreg('')
let sum = 0
let n = 1
let new = matchstr(input, '\[\s*\zs\d\+\%(\.\d\+\)\?\ze\s*u\s*]', 0, n)
while !empty(new)
let n += 1
let sum += str2float(new)
let new = matchstr(input, '\[\s*\zs\d\+\%(\.\d\+\)\?\ze\s*u\s*]', 0, n)
endwhile

echohl Identifier
echon 'Sum: ' . printf('%g', sum)
echohl None
return
endfunction
``````
• If there are two `[0.1u]` and `[0.1u]` on the same line, then this script only sums them as `0.1`, not `0.2`. Is there a way to make this aggregate the sum across one line as well as across multiple lines? Jun 3, 2017 at 16:08
• Sorry, I almost forgot to answer this. I've updated the answer with a slightly modified version of the `Analyse` function that does what you want. Jun 16, 2017 at 22:26

I've learned to avoid loop to speed-up processing times in vim scripts.

Given Karl's answer, my analyse function will be:

``````function! Analyse() abort
let tab = []
" I fill the tab with all the matching numbers thanks to substitute + \= + add(...)[0]