Javascript: comment créer dynamiquement des objets imbriqués en utilisant des noms d'objets donnés par un tableau

j'espère que quelqu'un peut m'aider avec ce Javascript.

j'ai un Objet appelé "Paramètres" et je voudrais écrire une fonction qui ajoute de nouveaux paramètres de l'objet.

le nom et la valeur du nouveau paramètre sont fournis sous forme de chaînes. La chaîne donnant le nom du paramètre est alors divisée par les underscores en un tableau. Le nouveau paramètre devrait être ajouté à l'objet "Settings" existant en créant de nouveaux objets imbriqués avec les noms donnés par chaque partie du tableau., sauf la dernière partie qui devrait être une chaîne de caractères donnant la valeur du paramètre. Je devrais alors être capable de me référer au paramètre et, par exemple, d'alerter sa valeur. Je peux le faire de manière statique comme ça...

var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");

Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;

alert(Settings.Modules.Mediaplayers.Video.Plugin);

... la partie qui crée les objets imbriqués le fait ...

Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";

cependant, comme le nombre de parties qui composent le nom de paramètre peut varier, par exemple un nom de fichier newSettingName pourrait être" Modules_Floorplan_Image_Src", j'aimerais le faire dynamiquement en utilisant une fonction telle que comme...

createSetting (newSettingNameArray, newSettingValue);

function createSetting(setting, value) {
    // code to create new setting goes here
}

est-ce que quelqu'un peut m'aider à trouver comment faire ça dynamiquement?

je suppose qu'il doit y avoir un pour...boucle là pour itterate à travers le tableau, mais je n'ai pas été en mesure de trouver un moyen de créer les objets imbriqués.

Si vous êtes arrivé jusqu'ici, c'merci beaucoup de prendre le temps de lire, même si vous ne pouvez pas aider.

45
demandé sur Brian Webster 2011-03-30 13:45:34

17 réponses

function assign(obj, keyPath, value) {
   lastKeyIndex = keyPath.length-1;
   for (var i = 0; i < lastKeyIndex; ++ i) {
     key = keyPath[i];
     if (!(key in obj))
       obj[key] = {}
     obj = obj[key];
   }
   obj[keyPath[lastKeyIndex]] = value;
}

Utilisation:

var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
50
répondu kennytm 2016-12-07 10:05:28

mettre une fonction, courte et rapide (Pas de récursion).

var createNestedObject = function( base, names ) {
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }
};

// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.

il omet des parties déjà existantes de la hiérarchie. Utile si vous n'êtes pas sûr que la hiérarchie ait déjà été créée.

Ou:

une version plus fantaisiste où vous pouvez affecter directement la valeur au dernier objet de la hiérarchie, et vous pouvez enchaîner les appels de fonction parce qu'elle renvoie le dernier objet.

// Function: createNestedObject( base, names[, value] )
//   base: the object on which to create the hierarchy
//   names: an array of strings contaning the names of the objects
//   value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
    // If a value is given, remove the last name and keep it for later:
    var lastName = arguments.length === 3 ? names.pop() : false;

    // Walk the hierarchy, creating new objects where needed.
    // If the lastName was removed, then the last object is not set yet:
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }

    // If a value was given, set it to the last name:
    if( lastName ) base = base[ lastName ] = value;

    // Return the last object in the hierarchy:
    return base;
};

// Usages:

createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.

var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300

createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400

Remarque: si votre hiérarchie doit être construit à partir de valeurs autres que les objets standards (c.-à-d. pas {}), Voir Aussi la réponse de TimDog ci-dessous.

Edit: utilise des boucles régulières, au lieu de for...in boucles. C'est plus sûr dans les cas où une bibliothèque modifie le prototype du réseau.

73
répondu jlgrall 2018-01-28 14:08:56
var nest = function(obj, keys, v) {
    if (keys.length === 1) {
      obj[keys[0]] = v;
    } else {
      var key = keys.shift();
      obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
    }

    return obj;
};

Exemple d'utilisation:

var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
8
répondu nerfologist 2015-02-26 16:59:17
const set = (obj, path, val) => { 
    const keys = path.split('.');
    const lastKey = keys.pop();
    const lastObj = keys.reduce((obj, key) => 
        obj[key] = obj[key] || {}, 
        obj); 
    lastObj[lastKey] = val;
};

Exemple:

const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
8
répondu Laurens 2016-08-31 12:14:06

voici un simple tweak à la réponse de jlgrall qui permet de définir distinct valeurs sur chaque élément de la hiérarchie imbriquée:

var createNestedObject = function( base, names, values ) {
    for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};

j'Espère que ça aide.

4
répondu TimDog 2012-10-30 04:14:20
const nest = (path, obj) => {
  const reversedPath = path.split('.').reverse();

  const iter = ([head, ...tail], obj) => {
    if (!head) {
      return obj;
    }
    const newObj = {[head]: {...obj}};
    return iter(tail, newObj);
  }
  return iter(reversedPath, obj);
}

Exemple:

const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
4
répondu Aliaksandr Sushkevich 2018-02-12 17:00:10

utiliser ES6 est plus court. Définissez votre chemin d'accès dans un tableau. tout d'abord, vous devez inverser le tableau, pour commencer le remplissage de l'objet.

let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});
1
répondu Diego Molina 2018-08-05 00:22:50

essayez d'utiliser une fonction récursive:

function createSetting(setting, value, index) {
  if (typeof index !== 'number') {
    index = 0;
  }

  if (index+1 == setting.length ) {
    settings[setting[index]] = value;
  }
  else {
    settings[setting[index]] = {};
    createSetting(setting, value, ++index);
  }
}
0
répondu sv_in 2011-03-30 10:01:18

je pense que c'est plus court:

Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");

a = Settings;
for (var i = 0 in newSettingNameArray) {
    var x = newSettingNameArray[i];
    a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
    a = a[x];
}
0
répondu Deele 2011-03-30 10:04:09

j'ai trouvé que la réponse de @jlgrall était bonne mais après l'avoir simplifiée, elle ne fonctionnait pas dans Chrome. Voici ma réparation si quelqu'un veut une version allégée:

var callback = 'fn.item1.item2.callbackfunction',
    cb = callback.split('.'),
    baseObj = window;

function createNestedObject(base, items){
    $.each(items, function(i, v){
        base = base[v] = (base[v] || {});
    });
}

callbackFunction = createNestedObject(baseObj, cb);

console.log(callbackFunction);

j'espère que c'est utile et pertinent. Désolé, je viens de détruire cet exemple...

0
répondu Must Impress 2015-01-28 18:59:34

vous pouvez définir vos propres méthodes D'objet; aussi j'utilise le underscore pour la brièveté:

var _ = require('underscore');

// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
    if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
    var tmpo = this;
    while (i = addr.shift())
        tmpo = tmpo[i];
    return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
    if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
    this.pick(_.initial(addr))[_.last(addr)] = val;
};

Exemple d'utilisation:

var obj = { 
           'foo': {
                   'bar': 0 }}

obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
0
répondu Keng 2015-05-27 15:07:01

J'apprécie que cette question soit très ancienne! Mais après être tombé sur un besoin de faire quelque chose comme ça dans le noeud, j'ai fait un module et l'ai publié à npm. Nestob

var nestob = require('nestob');

//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();

//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});

console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
  orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }

//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome

//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols

//You can also use nestob to modify objects not created using nestob
var normalObj = {};

nestob.setNested(normalObj, 'running.out.of', 'words');

console.log(normalObj);
//{ running: { out: { of: 'words' } } }

console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
0
répondu Chrift 2015-08-20 12:33:16

un extrait pour ceux qui ont besoin de créer des objets imbriqués avec le soutien de touches array pour définir une valeur à la fin du chemin. Le chemin est la chaîne comme:modal.product.action.review.2.write.survey.data. Basé sur la version jlgrall.

var updateStateQuery = function(state, path, value) {
    var names = path.split('.');
    for (var i = 0, len = names.length; i < len; i++) {
        if (i == (len - 1)) {
            state = state[names[i]] = state[names[i]] || value;
        }
        else if (parseInt(names[i+1]) >= 0) {
            state = state[names[i]] = state[names[i]] || [];
        }
        else {
            state = state[names[i]] = state[names[i]] || {};
        }
    }
};
0
répondu Vyacheslav Voronchuk 2016-01-05 22:25:34

Jeu De Données Imbriquées:

function setNestedData(root, path, value) {
  var paths = path.split('.');
  var last_index = paths.length - 1;
  paths.forEach(function(key, index) {
    if (!(key in root)) root[key] = {};
    if (index==last_index) root[key] = value;
    root = root[key];
  });
  return root;
}

var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}

Obtenir Des Données Imbriquées:

function getNestedData(obj, path) {
  var index = function(obj, i) { return obj && obj[i]; };
  return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
0
répondu Cody Moniz 2016-04-21 19:12:09

Eval est probablement exagéré mais le résultat est simple à visualiser, sans boucles emboîtées ou de récursion.

 function buildDir(obj, path){
   var paths = path.split('_');
   var final = paths.pop();
   for (let i = 1; i <= paths.length; i++) {
     var key = "obj['" + paths.slice(0, i).join("']['") + "']"
     console.log(key)
     eval(`${key} = {}`)
   }
   eval(`${key} = '${final}'`)
   return obj
 }

 var newSettingName = "Modules_Video_Plugin_JWPlayer";
 var Settings = buildDir( {}, newSettingName );

en gros vous écrivez progressivement une chaîne de caractères "obj['one']= {}", "obj['one']['two']"= {} et evaling;

0
répondu lonewarrior556 2017-01-19 20:59:20

essaye ceci: https://github.com/silkyland/object-to-formdata

var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
             a:1,
             b:[
                {c: 3},
                {d: 4}
             ]
})

Résultat :

fd = [
       a => 1,
       b => [
         c => 3,
         d => 4
       ]
]
0
répondu Manioz 2017-08-11 10:36:15

j'adore cette ES6 immuable façon de définir la valeur sur le champ imbriqué:

const setValueToField = (fields, value) => {
  const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
  return fields.reduceRight(reducer, {});
};

Et ensuite l'utiliser dans la création de votre objet cible.

const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
0
répondu Vadim Shvetsov 2018-06-20 15:00:49