javascriptobjectaccessorkvc

Access property via it's keyPath in Javascript?


I have

data = 
{
    'first': {
        'number': 1,
        'text': 'Ya.'
    },
    'second': {
        'number': 10,
        'text': 'Da.'
    }
};

And I really want to access it like:

number = data['first.number'];

Actually in a more flexible way, like:

numberOrText = data[memberName+'.'+propertyName];

Is there any lightweight library, or snippet you can suggest? This is - https://github.com/martinvl/KVCObject - so cool, but a bit overhead for this.


Solution

  • Based on @dandavis pretty simple suggestions, I can set up accessors as prototype properties.

    No eval, also leave Object.prototype untouched in terms of enumerating using Object.defineProperty.

    The solution actually goes like this:

    function stringContains(string, value)
    { return string.indexOf(value) != -1; }
    
    Object.defineProperty(Object.prototype, "setValueForKey", { value: function(value, key)
    { this[key] = value; }});
    
    Object.defineProperty(Object.prototype, "setValueForKeyPath", { value: function(value, keyPath)
    {
        if (keyPath == null) return;
        if (stringContains(keyPath, '.') == false) { this.setValueForKey(value, keyPath); return; }
    
        var chain = keyPath.split('.');
        var firstKey = chain.shift();
        var shiftedKeyPath = chain.join('.');
    
        this[firstKey].setValueForKeyPath(value, shiftedKeyPath);
    }});
    
    Object.defineProperty(Object.prototype, "getValueForKey", { value: function(key)
    { return this[key]; }});
    
    Object.defineProperty(Object.prototype, "getValueForKeyPath", { value: function(keyPath)
    {
        if (keyPath == null) return;
        if (stringContains(keyPath, '.') == false) { return this.getValueForKey(keyPath); }
    
        var chain = keyPath.split('.');
        var firstKey = chain.shift();
        var shiftedKeyPath = chain.join('.');
    
        return this[firstKey].getValueForKeyPath(shiftedKeyPath);
    }});
    

    Test are fine:

    data = {
        'name' : 'data',
        'first': {
            'number': 1,
            'text': 'Ya.',
            'meta' : {
                'lang' : 'en'
            }
        },
        'second': {
            'number': 10,
            'text': 'Ba.',
            'meta' : {
                'lang' : 'en'
            }
        },
        'third': {
            'number': 100,
            'text': 'Da.',
            'meta' : {
                'lang' : 'hu'
            }
        }
    };
    
    data.setValueForKey('chunk', 'name');
    data.setValueForKeyPath('blob', 'name');
    
    var thirdLanguage = data.getValueForKeyPath('third.meta.lang');
    data.setValueForKeyPath(thirdLanguage, 'first.meta.lang');
    data.setValueForKeyPath(thirdLanguage, 'second.meta.lang');
    
    log(data);
    

    Output is the same with hu as language in every data member.