Codage de valeur de clé d'objet Javascript. Définition dynamique d'une valeur imbriquée

Je travaille sur une petite bibliothèque qui me permet de coder des valeurs clés de base avec des objets. Disent que j'ai l'objet suivant:

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

Et j'ai la fonction JavaScript suivante:

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

Et en cours d'utilisation:

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

Cela fonctionne, mais j'aimerais accomplir ce qui précède sans utiliser eval . Est-ce possible, ou est-ce que eval est le meilleur moyen d'y aller?

MODIFIER:

REMARQUE, on peut supposer que la valeur que vous définissez existe, au moins pour la profondeur du chemin – 1. Je me préoccupe davantage de définir la valeur d'un objet existant.

La 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 

Oui, c'est facile. obj.prop = val est identique à obj['prop'] = val afin que vous puissiez 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 la réussite que vous obtiendrez avec le 3ème niveau (si cela est logique). Obj.prop.prop ne peut être référencé par obj ['prop.prop'] et devra plutôt être référencé par Obj ['prop'] ['prop'], donc setData devrait être réécrit pour tenir compte de cela. Je n'en ai pas beaucoup de chance.

Edit: J'ai fait un qui (pour moi) le définit comme désiré, mais pas plus loin. Malheureusement, si vous nichez plus profondément que vos exemples, je ne vois pas une véritable raison d'abandonner eval . Je n'ai pas fait de benchmarks, mais à un moment donné, les calculs seront plus coûteux que l' eval (ce qui est assez difficile à accomplir).

C'est ce à quoi je suis venu, ce qui mettra au moins 1 niveau de profondeur; C'est-à-dire qu'il fonctionnera avec key2.nested1 mais pas key2.nested1.i_love_nesting , si cela a du 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; } }; 

J'espère que cela t'aides. Je n'aurais peut-être pas écrit ceci de la manière la plus efficace, cependant …

 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; } 

Je pense que ce problème devient plus facile et plus naturel à résoudre si vous pouvez être 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 d'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 revenez simplement au 2ème arg de l'appel setData , jusqu'à ce que vous trouviez la valeur. Chaque fois que vous reculez, vous décidez dans l'arbre de data au point spécifié.

Cette fonction peut facilement être modifiée pour accepter la notation de points que vous avez spécifiée, mais pour moi, c'est une approche plus propre et plus naturelle (plus, String ops est lent).

À votre santé

Inspiré de @ user1416920, j'ai fait ceci:

 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, c'est assez explicatif ^^.