1

Give the following dictionary:

let mydict = {'foo': {'a': 1, 'b': 2}, 'bar': {'c': 3, 'd': 4}}

And a list of keys:

let myfilter = ['foo', 'asdf']

How would one do a concise "left outer join (if null)" or filter and remove any items in mydict with key in myfilter?

"Long" version as a loop:

for key in myfilter
    if has_key(mydict, key)
        call remove(mydict, key)
    endif
endfor

Which would yield:

:echo mydict
{'bar': {'c': 3, 'd': 4}}

It seems some combination of filter({expr1}, {expr2}) should do the trick, but haven't been able to come up with a {expr2} that uses v:key and successfully returns 0 when defined in myfilter and otherwise silently errs/return something other than 0.

4

It's possible to use index({list}, {expr} ..) for this:

let mydict = {'foo': {'a': 1, 'b': 2}, 'bar': {'c': 3, 'd': 4}}
let myfilter = ['foo', 'asdf']

call filter(mydict, 'index(myfilter, v:key) == -1')

index() returns -1 when {expr} is not found in {list} and otherwise the lowest index in {list}.

:echo index(['foo'], 'foo') == -1
0

filter() removes the item from the List when result of {expr2} evaluates to 0.


It's also possible to do something similar with dict keys as filter for another dict, but by using has_key({dict}, {key}):

let mydict = {'foo': {'a': 1, 'b': 2}, 'bar': {'c': 3, 'd': 4}}
let myfilter = {'foo': '5', 'asdf': '6'}

call filter(mydict, '!has_key(myfilter, v:key)')

has_key() returns 1 if {dict} has an entry with key {key}, zero otherwise.

  • 1
    I believe you can simplify the latter version to: call filter(mydict, '!has_key(myfilter, v:key)'). – Karl Yngve Lervåg Sep 4 '17 at 11:08
  • 1
    You can indeed @KarlYngveLervåg, thanks! Nice seeing you around by the way, had great use for LaTeX-Box back during my studies :) – timss Sep 4 '17 at 13:59
  • 1
    Good to hear! (Although you should check out vimtex now, if you still work with LaTeX.) – Karl Yngve Lervåg Sep 5 '17 at 13:10
  • @KarlYngveLervåg I've saved it for the future and it looks pretty sweet, but not much LaTeX these days I'm afraid :) – timss Sep 5 '17 at 13:51
2

If you really want to follow the destructive remove() path, you could also execute the following convoluted expression:

echo map(copy(myfilter), 'has_key(mydict, v:val) ? remove(mydict, v:val) : mydict')[0]

It may be faster with big dictionaries and small list of keys, but honestly, @timss' solution based on filter() + index() is the way to go. Both solutions will be much faster than any :for based solution.

  • Thanks, I wasn't sure if using filter() + index() was the way to go and felt a bit hacky, but I found no other answer to my question when searching online. Your solution is pretty cool too, and logically "closer" to something I'd originally figure one would do; remove by comparing vals of myfilter rather than some function on the myfilter list itself. Nice alternative though! – timss Sep 4 '17 at 6:16
  • Well, I find it very hacky as well. I say "this is the way to go" because it does the job well, and because in my experiments, index() is the fastest around for this kind of jobs. – Luc Hermitte Sep 4 '17 at 9:24
  • I'm curious: Is index() faster than has_key()? – Karl Yngve Lervåg Sep 4 '17 at 11:06
  • 1
    @KarlYngveLervåg There are not exactly comparable as you'd need to convert the reference elements either from a dictionary to list of keys (keys(dict)), or from list to a dictionary (map(copy(list), extend(dict, {v:val: 1})). Whether we'd use one or the other would really depends on the type of the original data. When we have the choice, I suspect has_key() to be faster on big collection of data. I'm not sure I had ever benchmarked one against the other, except to answer this question on collections of two elements. And here it's not just has_key(). It's has_key() + remove(). – Luc Hermitte Sep 4 '17 at 11:24
  • I meant as a response to "@timss' solution based on filter() + index() is the way to go.". Would not filter() + has_key() be better? – Karl Yngve Lervåg Sep 5 '17 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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