2013-02-15 5 views
5

Il s'agit d'une question inverse à this question.Créer un objet avec la notation par points

Étant donné un objet x={a:1,b:2} et une chaîne c.d=3, modifier l'objet x à ce qui suit:

{ 
    a:1, 
    b:2, 
    c:{ 
    d:3 
    } 
} 

Je suis à la recherche d'une solution qui n'utilise pas eval. Le cas d'utilisation est comme suit:

x étant un objet de configuration, nous appelons: config.set("music.shuffle",true)

Maintenant, music.shuffle doit être analysé en quelque sorte et ajouté à l'objet interne x dans la fonction config.set, de sorte que x ressemble à quelque chose comme:

x={a:1,b:2,music:{shuffle:true}} 
+0

Si C est une chaîne, alors vous allez devoir essayer de l'analyser manuellement dans un objet. Je peux presque garantir que cela sera plus facile à faire lorsque vous créerez la chaîne C et que vous ne devriez pas avoir à faire d'analyse manuelle. S'il vous plaît montrer cette section de votre code afin que je puisse aider plus loin – Undefined

+0

Est-ce que la chaîne est garantie '' dot.separated.string = value'' et quelles restrictions sont placées sur la valeur? Est-ce un type JSON par exemple? –

+0

La chaîne est garantie être point.seperated et il n'y a aucune contrainte sur la valeur. – Nemo

Répondre

10

du haut de ma tête, je suppose que vous pouvez faire quelque chose comme ceci:

function addValueToObj(obj, newProp) { 
    newProp = newProp.split("=");  // separate the "path" from the "value" 

    var path = newProp[0].split("."),  // separate each step in the "path" 
     val = newProp.slice(1).join("="); // allow for "=" in "value" 

    for (var i = 0, tmp = obj; i < path.length - 1; i++) { 
     tmp = tmp[path[i]] = {};  // loop through each part of the path adding to obj 
    } 
    tmp[path[i]] = val;    // at the end of the chain add the value in 
} 

var x = {a:1, b:2}; 
addValueToObj(x, "c.d=3"); 
// x is now {"a":1,"b":2,"c":{"d":"3"}} 
addValueToObj(x, "e.f.g.h=9=9"); 
// x is now {"a":1,"b":2,"c":{"d":"3"},"e":{"f":{"g":{"h":"9=9"}}}} 

Démo: http://jsfiddle.net/E8dMF/1/

+4

pour le demandeur bien sûr, mais je recommanderais la mise à jour afin que vous n'obstruez pas les propriétés qui sont déjà présentes. par exemple, si x = {a: {b: 3}} et que j'ajoute AddValueToObj (x, "a.c = 4") alors b disparaît. –

4

Je crois que setObject de dojo fait ce que vous voulez. Si vous (naturellement) ne voulez pas rentrer tout le dojo alors je vous recommande d'examiner leur source (disponible gratuitement) ou de charger juste la base (seulement 4k) via AMD. Il ressemble à ceci:

function setObject(name, value, context) { 
    var parts=name.split("."), 
    p=parts.pop(); 
    for(var i=0, j; context && (j=parts[i]); i++){ 
     context = (j in context ? context[j] : context[j]={}); 
    } 
    return context && p ? (context[p]=value) : undefined; // Object 
} 

Donc, dans votre cas, vous faire:

x={a:1,b:2}; 
setObject("c.d", 3, x); 

Avertissement: à moins que vous ne jamais traiter les affaires triviales, je vous invite à aller toujours vérifier le plein l'implémentation de dojo, qui traite des cas où aucun contexte n'est fourni, etc.

2

Voici une version très commentée qui devrait être assez simple à comprendre.

// stores the configured data 
configStore = {}; 

config = { 
    set: function(keyValueString) { 

    // Split the string by the = 
    var pair = keyValueString.split('='); 

    // left of the = is the key path 
    var keyPath = pair[0]; 

    // right of the = is the value to set 
    var value = pair[1]; 

    // split keyPath into an array of keys 
    var keys = keyPath.split('.'); 
    var key; // used in loop 

    // the current level of object we are drilling into. 
    // Starts as the main root config object. 
    var currentObj = configStore; 

    // Loop through all keys in the key path, except the last one (note the -1). 
    // This creates the object structure implied by the key path. 
    // We want to do something different on the last iteration. 
    for (var i=0; i < keys.length-1; i++) { 

     // Get the current key we are looping 
     key = keys[i]; 

     // If the requested level on the current object doesn't exist, 
     // make a blank object. 
     if (typeof currentObj[key] === 'undefined') { 
     currentObj[key] = {}; 
     } 

     // Set the current object to the next level of the keypath, 
     // allowing us to drill in. 
     currentObj = currentObj[key]; 
    } 

    // Our loop doesn't handle the last key, because that's when we 
    // want to set the actual value. So find the last key in the path. 
    var lastKey = keys[keys.length-1] 

    // Set the property of the deepest object to the value. 
    currentObj[lastKey] = value; 
    } 
}; 

// Do it. 
config.set('omg.wtf.bbq=123') 

// Check it. 
alert(configStore.omg.wtf.bbq); // 123 
2

devait faire quelque chose aujourd'hui similaire, voici une autre solution. Certainement pourrait utiliser un peu de nettoyage, mais il fait l'affaire. Cela étendra un objet existant et n'effacera aucune donnée à condition que l'entrée soit valide.

Il n'y a pas de validation, donc vous pouvez définitivement remplacer les clés si vous transmettez des données erronées.

// @param object orig the object to extend 
// @param array keyParts the.key.path split by "." (expects an array, presplit) 
// @param mixed value the value to assign 
// @param object scoped used by the recursion, ignore or pass null 
function unflatten(orig, keyParts, value, scoped) { 
    if (!scoped) { 
     scoped = orig; 
    } 

    var nextKey = keyParts.shift(); 

    if (keyParts.length === 0) { 
     scoped[nextKey] = value; 
     return orig; 
    } 

    if (!scoped[nextKey]) { 
     scoped[nextKey] = {}; 
    } 

    scoped = scoped[nextKey]; 
    return unflatten(orig, keyParts, value, scoped); 
} 

Le prototype de fonction peut être amélioré, mais répond à mes besoins. Appelez cela via:

var orig = { foo: 'hello world', bar: { baz: 'goodbye world' } }; 

// lets add the key "bar.cat.aww" with value "meow" 
unflatten(orig, "bar.cat.aww".split("."), "meow"); 
/* 
    orig is { 
    foo: "hello world", 
    bar: { 
     baz: "goodbye world", 
     cat: { 
     aww: "meow" 
     } 
    } 
    } 

*/ 

// we can keep calling it to add more keys 
unflatten(orig, "some.nested.key".split("."), "weeee"); 

/* 
    orig is { 
    foo: "hello world", 
    bar: { 
     baz: "goodbye world", 
     cat: { 
     aww: "meow" 
     } 
    }, 
    some: { 
     nested: { 
     key: "weeee" 
     } 
    } 
    } 
*/ 
3

Vous pouvez le faire avec lodash.set()

 
> l=require('lodash') 
> x={a:1,b:2}; 
{ a: 1, b: 2 } 
> l.set(x, 'c.d', 3) 
{ a: 1, b: 2, c: { d: 3 } } 
0

Comment cela?

Il va créer ou copier/écraser un objet existant.

function expando(obj, base) { 
    return Object.keys(obj) 
     .reduce((clone, key) => { 
     key.split('.').reduce((innerObj, innerKey, i, arr) => 
      innerObj[innerKey] = (i+1 === arr.length) ? obj[key] : innerObj[innerKey] || {}, clone) 
     return clone; 
    }, Object.assign({}, base)); 
} 

console.log(expando({'a.b': 1})) // { a: { b : 1 }} 
console.log(expando({'b.c': 2}, { a: 1 })) // { a: 1, b: { c: 2 }} 
console.log(expando({'b.c.d': 2, 'e.f': 3}, { a: 1 })) // { a: 1, b: { c: { d: 2 } }, e: { f: 3}} 

REMARQUE: ES6 requis pour les fonctions de flèche et Object.assign().

Ne hésitez pas à jouer:

https://jsbin.com/setazahuce/1/edit?js,console

0

Qu'en est-ce:

function setObj (str, value, obj) { 
    var ref = obj, keys = str.split('.'); 
    while (keys.length) { 
     var currentKey = keys.shift(); 
     ref[currentKey] = keys.length ? (ref[currentKey] ? ref[currentKey] : {}) : value; 
     ref = ref[currentKey]; 
    } 
} 

Exemple avec un objet d'entrée en utilisant (peut-être des valeurs de forme extraites avec .serializeArray $)

var serializedInputs = [ 
    {name: 'fruits[1][name]', value: 'Banana'}, 
    {name: 'fruits[1][id]', value: '1'}, 
    {name: 'fruits[2][name]', value: 'Strawberry'}, 
    {name: 'fruits[2][id]', value: '2'}, 
    {name: 'fruits[3][name]', value: 'Raspberry'}, 
    {name: 'fruits[3][id]', value: '3'}, 
    {name: 'fruits[4][name]', value: 'Kiwi'}, 
    {name: 'fruits[4][id]', value: '4'}, 
    {name: 'fruits[5][name]', value: 'Mango'}, 
    {name: 'fruits[5][id]', value: '5'}, 
    {name: 'selected_fruit_id', value: '1'}, 
] 
// This variable holds the result 
var obj = {} 
serializedInputs.forEach(function(item) { 
    // Turning square brackets into dot notation 
    setObj(item.name.replace(/\]/g, '').replace(/\[/g, '.'), item.value, obj); 
}) 

Résultat

{ 
    "fruits": { 
     "1": { 
      "name": "Banana", 
      "id": "1" 
     }, 
     "2": { 
      "name": "Strawberry", 
      "id": "2" 
     }, 
     "3": { 
      "name": "Raspberry", 
      "id": "3" 
     }, 
     "4": { 
      "name": "Kiwi", 
      "id": "4" 
     }, 
     "5": { 
      "name": "Mango", 
      "id": "5" 
     } 
    }, 
    "selected_fruit_id": "1" 
} 
Questions connexes