1

The vimscript allows list union via the + operator, but the list subtraction doesn't come as nice as the union. Is there an easy way to filter a list by another list?

:echo [1, 2, 3] + [1, 10]
" [1, 2, 3, 1, 10]


:echo [1, 2, 3] - [1, 10]
" E730: using List as a String

" What I want: [2, 3]

I tried to write a call with filter() and map() but could not go very far.

2 Answers 2

5

It is a little bit faster to use a string expression than a lambda (and arguably easier to understand in this case),

call filter(a, 'index(b, v:val)<0')

This naive implementation is O(N^2) but is still fairly fast in practice as index() is implemented in C. If you have a very large number of elements in the RHS, it may be faster to use a pre-computed hash table (Dict). However, building the table (especially using a for loop) is likely to destroy any benefit.

" [1, 2, 3] - [1, 10]

let a = [1, 2, 3]
let b = [1, 10]

let hash = {}
for bv in b
    let hash[bv] = 1
endfor
" a more obtuse way is..
" call mapnew(b, 'extend(hash, {v:val: 1})')

call filter(a, '!has_key(hash, v:val)')
1
  • 1
    +1 For the string > lambda regarding performances -- at least with earlier versions. However, building the hash with a for loop is likely to be much slower than the N² naive implementation based on index with the quantity of data we usually handle with Vim (at most 10k-50k that come from things like taglist()) Commented Jan 24, 2021 at 17:52
3

To keep it simple, I assume all elements are different (i.e. it's "set difference"):

:call filter(list1, {_, v -> index(list2, v) < 0})

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.