I would like to create a dictionary variable for a vimL script. That dictionary will hold information related to a folder structure. However, I have a bit of hard time designing this variable and also I came across some difficulties while handling the information stored in that variable. Below is an example dictionary variable.

let g:myTree = { 
        \ 'base' : {
            \ 'path' : '__base',
            \ 'main' : {'path' : '__base/__main'},
            \ 'config' : {
                \ 'path' : '__base/__/config',
                \ 'modules' : {'path' : '__base/__config/__modules'},
                \ 'settings': {
                    \ 'path' : '__base/__config/__settings',
                    \ 'gui' : {
                        \ 'path' : '__base/__config/__settings/__gui',
                        \ 'linux' : {'path' : '__base/__config/__settings/__gui/__linux'},
                        \ },
                    \ },
                \ },
            \ },
        \ }

The idea was to be able to access the data bits with the . notation. For example g:myTree.base should return __base. However, I have noticed that I need an additional key path in order to be able to package the nested information, so the access is done through g:myTree.base.path for __base.

The first questions would be:

Is there a way to avoid the use of path and directly access the data in the following format:

g:myTree.base for __base

g:myTree.base.main for __base/__main

g:myTree.base.config.modules for __base/__config/__modules

g:myTree.base.config.settings.gui.linux for __base/__config/__settings/__gui/__linux

The second question would be:

How do we iterate and access (or modify) the data bits stored in the path key throughout the whole depth of the dictionary. I have encountered a problem due to the fact that the path key stores a string value but the next key would be storing a dictionary value. This creates a variable mismatch when iterating through the dictionary. What would be the solution to this?


  • For the second issue, maybe :h type() could help.
    – VanLaser
    Mar 13 '16 at 19:07
  • @muru The intention was the ability to access the root value of any nested dictionary without the need for an extra container, so that there is less typing. I wanted to be sure that there is no special method that will return the data from the top of each dictionary entry. Like for example: foo = {'_' : 'house', 'bar': 'apple'} so that foo will return house and foo.bar will return apple
    – mbilyanov
    Mar 13 '16 at 19:30
  • @symbolix keys are unordered. As far as Vim is concerned, bar is just as likely to be the top as _.
    – muru
    Mar 13 '16 at 19:31
  • @muru Sure. By top I mean, like the root of each nest, like a secret key that would act as the first element, without the need for an extra storage string. But, you have already answered my question, there are no default (reserved) methods for the dictionaries. Like in the example above, you would have a '_' key with the house value in there but you would not need to type foo._ to access the house but only foo will be enough.
    – mbilyanov
    Mar 13 '16 at 19:37
  • If you want, you can define a function for each dict that returns a given value, so you'd call foo.P() instead of foo.path.
    – muru
    Mar 13 '16 at 19:39

I don't think you can set a key as the "default" for a dictionary. You should probably look into the other supported scripting languages, like Python or Ruby.

As for the second problem, you can check the type:

function! RecurseForPath(dict)
    for key in keys(a:dict)
        if key == 'path'
            echo a:dict[key]
        elseif type(a:dict[key]) == type({})
            call RecurseForPath(a:dict[key])

call RecurseForPath(myTree)

From :h type:

type({expr})    The result is a Number, depending on the type of {expr}:
                        Number:     0
                        String:     1
                        Funcref:    2
                        List:       3
                        Dictionary: 4
                        Float:      5
                        Boolean:    6 (v:false and v:true)
                        None        7 (v:null and v:none)
                        Job         8
                        Channel     9
                To avoid the magic numbers it should be used this way: 
                        :if type(myvar) == type(0)
                        :if type(myvar) == type("")
                        :if type(myvar) == type(function("tr"))
                        :if type(myvar) == type([])
                        :if type(myvar) == type({})
                        :if type(myvar) == type(0.0)
                        :if type(myvar) == type(v:false)
                        :if type(myvar) == type(v:none)

The output, with the variable from the question, is:


The idea was to be able to access the data bits with the . notation. For example g:myTree.base should return __base. However, I have noticed that I need an additional key path in order to be able to package the nested information, so the access is done through g:myTree.base.path for __base.

This is not really how dictionaries (or hashes, or tables, or whatever your preferred term is) work in any language as far as I know. In fact, I can't see how it could conceivably work.

How would you disambiguate between calling g:myTree.base when you really want to get this dict, and g:myTree.base when you want g:myTree.base.__base? There is no real way to to that.

Even in more advanced languages with operator overloading, such as Python or Ruby, it would actually be a challenge to implement this; in Python we could override the __getitem()__ method, but that doesn't get called when we merely reference a dict. I would be surprised if anyone would be able to come up with a working implementation of this to be honest.

What you can do is assign functions to dictionaries to return the values you want; this is sort of like "poor man's object oriented programming".

I'm not entirely sure what your task is or what you're trying to do, so I'm not sure if this is the "right tool for the right job", but it could very well be.

A simple example might look like:

fun! s:full_path() dict
    return self.base . self.path

let g:my_tree = {
    \ 'base': '/home/martin/',
    \ 'path': '.vim',
    \ 'full_path': function("s:full_path"),

And when calling :echo g:my_tree.full_path() you will get /home/martin/.vim. See :help Dictionary-function and :help Funcref for the full documentation.

You could emulate part of the behaviour described in your answer; but I'm not sure if that would be a sane thing to do.

Above all, remember Eric S. Raymond's "Rule of Representation" from The Art of Unix Programming (it's free!):

Rule of Representation: Fold knowledge into data, so program logic can be stupid and robust.

Even the simplest procedural logic is hard for humans to verify, but quite complex data structures are fairly easy to model and reason about. To see this, compare the expressiveness and explanatory power of a diagram of (say) a fifty-node pointer tree with a flowchart of a fifty-line program. Or, compare an array initializer expressing a conversion table with an equivalent switch statement. The difference in transparency and clarity is dramatic. See Rob Pike's Rule 5.

Data is more tractable than program logic. It follows that where you see a choice between complexity in data structures and complexity in code, choose the former. More: in evolving a design, you should actively seek ways to shift complexity from code to data.

The Unix community did not originate this insight, but a lot of Unix code displays its influence. The C language's facility at manipulating pointers, in particular, has encouraged the use of dynamically-modified reference structures at all levels of coding from the kernel upward. Simple pointer chases in such structures frequently do duties that implementations in other languages would instead have to embody in more elaborate procedures.

As for the second part of your question, I believe muru already did a good job of answering that, so I'll not repeat it here ;-)

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.