Map and filter

Vimscript supports map and filter—higher-order functions (aka functions that operate on functions). Both of these functions take either a list or a dictionary as a first argument, and a function as a second.

For instance, if we wanted to filter out every animal name that is not proper, we would write a filter function:


function IsProperName(name)
if a:name =~? '(Mr|Miss) .+'
return 1
endif
return 0
endfunction

IsProperName will return 1 (true) if the name starts with Mr or Miss (which is, as we know, a proper form to address an animal), and 0 (false) otherwise.

Now, given the following dictionary:

let animal_names = {
'cat': 'Miss Cattington',
'dog': 'Mr Dogson',
'parrot': 'Polly'
}

We will write a filter function that will only leave key or value pairs with proper names:

call filter(animal_names, 'IsProperName(v:val)')

And we'll do this to see that it worked:

:echo animal_names
{'cat': 'Miss Cattington', 'dog': 'Mr Dogson'}

If you're coming from other programming languages, this syntax probably feels somewhat awkward. The second argument to the filter function is a string, which gets evaluated for every key value pair of the dictionary. Here, v:val will get expanded to the dictionary value (while v:key could be used to access the key).

The second argument to filter can also be a function reference. Vim lets you reference a function like this:

let IsProperName2 = function('IsProperName')

Now, you can call IsProperName2 just like you would IsProperName:

:echo IsProperName2('Mr Dogson')
1

This can be used to pass functions around as arguments to any function:

function FunctionCaller(func, arg)
return a:func(a:arg)
endfunction

Try running it:

:echo FunctionCaller(IsProperName2, 'Miss Catington')
1

Armed with this knowledge, we can also pass a function reference as a second argument to the filter function. However, if we decide to pass a function reference, we have to change our original function to take two arguments: the key and the value for the dictionary (in that order):

function IsProperNameKeyValue(key, value)
if a:value =~? '(Mr|Miss) .+'
return 1
endif
return 0
endfunction

Now, we can execute the filter function as follows:

call filter(animal_names, function('IsProperNameKeyValue'))

And to validate that it works, let's echo the animal_names dictionary:

:echo animal_names
{'cat': 'Miss Cattington', 'dog': 'Mr Dogson'}

When operating on lists, v:key refers to a item index, and v:val refers to the item value.

The map function behaves in a similar manner. It lets you modify each list item or dictionary value.

For example, let's make every name proper in the following list:

let animal_names = ['Miss Cattington', 'Mr Dogson', 'Polly', 'Meowtington']

In this exercise, we'll reuse the IsProperName function from the earlier example.

Lambdas come in especially useful with this type of function. Here's how we prefix 'Miss ' to a name if it's not proper:

call map(animal_names,
{key, val -> IsProperName(val) ? val : 'Miss ' . val})

Verify the results are as expected:

:echo animal_names
['Miss Cattington', 'Mr Dogson', 'Miss Polly, 'Miss Meowtington']

This Lambda is the equivalent of the following:

function MakeProperName(name)
if IsProperName(a:name)
return a:name
endif
return 'Miss ' . a:name
endfunction
call map(animal_names, 'MakeProperName(v:val)')

The map function can be called with a function reference the same way a filter is. The mapping function will still take two values (key and value for dictionaries, and index and value for lists).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.196.184