2010-01-14 6 views
2

Je travaille sur une petite bibliothèque qui me permet de coder des valeurs de base avec des objets. Dire que j'ai l'objet suivant:Codage de valeur de clé d'objet Javascript. Définition dynamique d'une valeur imbriquée

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } }; 

Et je la fonction JavaScript suivante:

var setData = function(path, value) { 
    eval("data." + path + "= value;"); 
}; 

Et en cours d'utilisation: cependant

setData("key1", "updated value1"); // data.key1 == "updated value1" 
setData("key2.nested1", 99); // data.key2.nested1 == 99 

Cela fonctionne, je voudrais accomplir la ci-dessus sans utiliser eval. Est-ce possible, ou est eval le meilleur chemin à faire?

EDIT:

NOTE on peut supposer la valeur que vous configurez existe, au moins pour la profondeur de chemin - 1. Je suis plus préoccupé par définir la valeur d'un objet existant.

Répondre

16

récursivité est ce dont vous avez besoin:

function setData(key,val,obj) { 
    if (!obj) obj = data; //outside (non-recursive) call, use "data" as our base object 
    var ka = key.split(/\./); //split the key by the dots 
    if (ka.length < 2) { 
    obj[ka[0]] = val; //only one part (no dots) in key, just set value 
    } else { 
    if (!obj[ka[0]]) obj[ka[0]] = {}; //create our "new" base obj if it doesn't exist 
    obj = obj[ka.shift()]; //remove the new "base" obj from string array, and hold actual object for recursive call 
    setData(ka.join("."),val,obj); //join the remaining parts back up with dots, and recursively set data on our new "base" obj 
    }  
} 

setData("key1", "updated value1"); // data.key1 == "updated value1" 
setData("key2.nested1", 99); // data.key2.nested1 == 99 
+1

Je souhaite que je pourrais upvote ceci 100 fois - c'était exactement ce que je cherchais! Je vous remercie! –

+0

** Merci ** beaucoup! Je ne peux pas commencer à vous dire combien cela m'a aidé. –

+0

Ceci est une excellente réponse. J'ai fait une version sans argument 'obj' et avec la syntaxe ES6 pour la lisibilité. https://gist.github.com/sungwoncho/8caf7f3d9e56741c0631cda134e01832 –

4

Oui, c'est facile. obj.prop = val est le même que obj['prop'] = val vous pouvez donc réécrire setData comme ceci:

var setData = function (path, value) { 
    data[path] = value; 
}; 

Et cela devrait le faire.

Cependant, je ne suis pas sûr de savoir combien de succès vous aurez avec le 3ème niveau (si cela a du sens.) Obj.prop.prop ne peut pas être référencé par obj ['prop.prop'] et aura plutôt pour être référencé par obj ['prop'] ['prop'], setData devrait être réécrit pour en tenir compte. Je n'ai pas beaucoup de chance avec ça, cependant. Editer: J'en ai fait un qui (pour moi) le définit comme vous le souhaitez, mais pas plus loin que ça. Malheureusement, si vous nichez plus profond que vos exemples, je ne vois pas de véritable raison d'abandonner eval. Je n'ai pas fait de benchmarks, mais à un certain moment, les calculs seront plus coûteux que eval (ce qui est assez difficile à réaliser.)

Voici ce que j'ai imaginé, qui leur donnera au moins 1 ' niveau 'profond; qui est, il travaillera avec key2.nested1 mais pas key2.nested1.i_love_nesting, si cela fait sens:

var setData = function (path, value) { 
    if (path.indexOf('.') != -1) { 
     path = path.split('.'); 
     for (var i = 0, l = path.length; i < l; i++) { 
      if (typeof(data[path[i]]) === 'object') { 
       continue; 
      } else { 
       data[path[i - 1]][path[i]] = value; 
      } 
     } 
    } else { 
     data[path] = value; 
    } 
}; 

Hope this helps. Je ne pourrais pas avoir écrit cela de la manière la plus efficace, mais ...

+0

Malheureusement, cela ne fonctionnera pas pour les valeurs imbriquées. Dans votre exemple: setData ("prop.nestedProp", "val"); entraînera la JSON de données suivantes: { "prop.nestedProp": "val"} Je veux: {prop: {nestedProp: "val"}} –

+0

Oui, je pris que quelques secondes après que je soumis le post. Il m'a fallu un peu pour avoir une belle fonction de travail, mais celle ci-dessus devrait faire l'affaire. Un choix alternatif pour votre bibliothèque pourrait être de les laisser accéder directement à 'l'objet', c'est-à-dire les laisser accéder à 'my_json_object.foo.bar'. – Reid

+0

La fonction complète (exclue pour simplifier) ​​déclenchera un événement si la valeur a changé. app.dataSet = function(path, value) { if (app.dataGet(path) == value) return value; eval("app._data." + path + "= value;"); app.trigger("data." + path); return app.dataGet(path); }; Malheureusement, cela doit fonctionner à une profondeur arbitraire. J'espérais qu'il y aurait une bonne solution au problème, laisser la fonction get (devra tester si elle est plus rapide, puis utiliser eval aussi). Merci pour l'aide. –

0
function setData(key,val,obj){ 
    keys = key.split(/\./); 
    to_set = keys.pop(); 
    keys.forEach(function(obj_name){ obj = obj[obj_name]; }); 
    obj[to_set] = val; 
} 
+0

Pourriez-vous fournir une description de ce que fait votre code? – cereallarceny

0

Je pense que ce problème devient plus facile et plus naturel de résoudre si vous le pouvez Soyez flexible dans la façon dont vous spécifiez votre chemin. Si au lieu de faire quelque chose comme key2.nested1 vous pouvez faire {key2:{nested1:{}}} (notation objet), il devient assez trivial:

function setData(subtree, path){ 
    var nodeName = Object.keys(path); 

    if(typeof path[nodeName] == 'object') 
     setData(subtree[nodeName], path[nodeName]); 
    else 
     subtree[nodeName] = path[nodeName]; 
} 

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } }; 

// For the initial call, 'subtree' is the full data tree 
setData(data, {key2:{nested1:99}}); 

Vous êtes tout simplement récursion dans le 2ème arg de l'appel setData, jusqu'à ce que vous trouviez la valeur. Chaque fois que vous récurnez, vous descendez dans l'arborescence data au point spécifié.

Cette fonction peut facilement être modifiée pour accepter la notation par points que vous avez spécifiée, mais pour moi, c'est une approche plus propre et plus naturelle (en plus, les opérations de chaînes sont lentes).

Vive

0

Inspiré de @ user1416920 J'ai fait celle-ci:

function setData(key,val,obj) 
{ 
    keys = key.split(/\./); 
    last = keys.pop(); 

    keys.forEach(function(key) 
    { 
     if(typeof obj[key] === "undefined") 
     obj[key] = {}; 
     obj = obj[key]; 
    }); 

    obj[last] = val; 
} 

Ce qu'il fait est assez explicite ^^.

Questions connexes