9 votes

Sélection dynamique profonde pour un objet JavaScript

Avec une seule propriété, c'est assez facile :

var jsonobj = {
    "test": "ok"
}
var propname = "test";
// Will alert "ok"
alert(jsonobj[propname]);

Mais ce que je veux faire, c'est utiliser une propriété imbriquée :

var jsonobj = {
    "test": {
        "test2": "ok"
    }
}
var propname = "test.test2";
// Alerts undefined
alert(jsonobj[propname]);

Existe-t-il un moyen de sélectionner une propriété "dynamique" imbriquée ? Je sais que je peux faire jsonobj.test.test2, mais le problème est que propname peut changer en une propriété qui va à 1, 2 ou 3 niveaux de profondeur (par exemple test, test.test2, ...).

12voto

James Points 56229
function resolve(cur, ns) {

    var undef;

    ns = ns.split('.');

    while (cur && ns[0])
        cur = cur[ns.shift()] || undef;

    return cur;

}

Par exemple

// 1:
resolve({
    foo: { bar: 123 }
}, 'foo.bar'); // => 123

// 2:
var complex = {
    a: {
        b: [
            document.createElement('div')
        ]
    }
};

resolve(complex, 'a.b.0.nodeName'); // => DIV

L'avantage de cette méthode est qu'elle ne déclenchera pas d'erreur si vous essayez d'accéder à quelque chose qui n'existe pas -- elle retournera gracieusement undefined .


EDIT :

Dans le commentaire, Andy a mentionné que cela n'entraîne pas d'erreurs là où on pourrait s'y attendre. Je suis d'accord sur le fait qu'obtenir undefined est un peu générique et il n'y a aucun moyen de savoir si votre valeur a vraiment été résolue. Donc, pour remédier à cela, essayez ceci :

var resolve = (function(){

    var UNRESOLVED = resolve.UNRESOLVED = {};
    return resolve;

    function resolve(cur, ns) {

        var undef;

        ns = ns.split('.');

        while (cur && ns[0])
            cur = cur[ns.shift()] || undef;

        if (cur === undef || ns[0]) {
            return UNRESOLVED;
        }

        return cur;

    }

}());

Il retournera un objet UNRESOLVED qui peut être vérifié comme suit :

var result = resolve(someObject, 'a.b.c');

if (result === resolve.UNRESOLVED) {...}

Ce n'est pas parfait, mais c'est (IMO) la meilleure façon de déterminer un espace de nom non résolu sans avoir à lancer des erreurs. Si vous voulez des erreurs, alors allez-y :

someObject.a.b.c; //...

3voto

sym3tri Points 2076

J'ai également implémenté ceci en utilisant une fonction récursive interne comme ceci :

function get(obj, ns) {            

    function recurse(o, props) {
        if (props.length === 0) {
            return o;
        }
        if (!o) {
            return undefined;
        }
        return recurse(o[props.shift()], props);
    }

    return recurse(obj, ns.split('.'));
}

Ceci retournera la valeur profonde de la propriété spécifiée par le paramètre ns, sinon retournera toujours undefined si elle n'existe pas ou s'il y a des problèmes en cours de route.

1voto

Mike Clark Points 4468

Vous pouvez écrire une petite fonction pour diviser la chaîne et accéder ensuite à chaque morceau à tour de rôle. Par exemple :

function getProperty(propname, object)
{
    var props = propname.split('.');
    var obj = object;
    for (var i=0; i<props.length; i++)
    {
       obj = obj[props[i]];
    }
    return obj;
}

Il est évident que cela nécessite un peu de codage supplémentaire pour vérifier les objets nuls, les propriétés valides, etc.

0voto

karim79 Points 178055

Cela fonctionne, mais utilise plutôt mal eval donc je suis no recommandant son utilisation :

var jsonobj = {
    "test": {
        "test2": "ok"
    }
}
var propname = "test.test2";
alert(eval("jsonobj." + propname));
​

Essayez-le ici : http://jsfiddle.net/TAgsU/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X