145 votes

Définir dynamiquement la propriété d'un objet imbriqué

J'ai un objet qui pourrait être de n'importe quel niveau de profondeur et pourrait avoir n'importe quelles propriétés existantes. Par exemple:

var obj = {
    db: {
        mongodb: {
            host: 'localhost'
        }
    }
};

Sur cela, j'aimerais définir (ou écraser) des propriétés de cette manière:

set('db.mongodb.user', 'root');
// ou:
set('foo.bar', 'baz');

Où la chaîne de propriété peut avoir n'importe quelle profondeur, et la valeur peut être de n'importe quel type/chose.
Les objets et les tableaux en tant que valeurs n'ont pas besoin d'être fusionnés, si la clé de propriété existe déjà.

L'exemple précédent produirait l'objet suivant:

var obj = {
    db: {
        mongodb: {
            host: 'localhost',
            user: 'root'
        }
    },
    foo: {
        bar: baz
    }
};

Comment puis-je réaliser une telle fonction?

12voto

webjay Points 983

Inspiré par la réponse de @bpmason1 :

fonction leaf(obj, path, value) {
  const pList = path.split('.');
  const key = pList.pop();
  const pointer = pList.reduce((accumulator, currentValue) => {
    if (accumulator[currentValue] === undefined) accumulator[currentValue] = {};
    return accumulator[currentValue];
  }, obj);
  pointer[key] = value;
  return obj;
}

const obj = {
  boats: {
    m1: 'lady blue'
  }
};
leaf(obj, 'boats.m1', 'lady blue II');
leaf(obj, 'boats.m2', 'lady bird');
console.log(obj); // { boats: { m1: 'lady blue II', m2: 'lady bird' } }

10voto

ron4ex Points 94

ES6 a aussi une façon assez cool de le faire en utilisant Nom de Propriété Calculée et Paramètre Restant.

const obj = {
  levelOne: {
    levelTwo: {
      levelThree: "Définissez celui-ci!"
    }
  }
}

const updatedObj = {
  ...obj,
  levelOne: {
    ...obj.levelOne,
    levelTwo: {
      ...obj.levelOne.levelTwo,
      levelThree: "Je suis maintenant mis à jour!"
    }
  }
}

Si levelThree est une propriété dynamique c'est-à-dire pour définir l'une des propriétés dans levelTwo, vous pouvez utiliser [nomDePropriété]: "Je suis maintenant mis à jour!"nomDePropriété contient le nom de la propriété dans levelTwo.

10voto

Henry Ing-Simmons Points 503

J'ai trouvé ma propre solution en utilisant du pur es6 et de la récursivité qui ne mute pas l'objet d'origine.

const setNestedProp = (obj = {}, [first, ...rest] , value) => ({
  ...obj,
  [first]: rest.length
    ? setNestedProp(obj[first], rest, value)
    : value
});

const result = setNestedProp({}, ["first", "second", "a"], 
"foo");
const result2 = setNestedProp(result, ["first", "second", "b"], "bar");

console.log(result);
console.log(result2);

9voto

brafdlog Points 31

Lodash a une méthode appelée update qui fait exactement ce dont vous avez besoin.

Cette méthode reçoit les paramètres suivants:

  1. L'objet à mettre à jour
  2. Le chemin de la propriété à mettre à jour (la propriété peut être profondément imbriquée)
  3. Une fonction qui retourne la valeur à mettre à jour (en donnant la valeur originale en tant que paramètre)

Dans votre exemple, cela ressemblerait à ceci:

_.update(obj, 'db.mongodb.user', function(originalValue) {
  return 'root'
})

3voto

Rehmat Points 588

J'avais besoin d'atteindre la même chose, mais en Node.js... Donc, j'ai trouvé ce joli module : https://www.npmjs.com/package/nested-property

Exemple:

var mod = require("nested-property");
var obj = {
  a: {
    b: {
      c: {
        d: 5
      }
    }
  }
};
console.log(mod.get(obj, "a.b.c.d"));
mod.set(obj, "a.b.c.d", 6);
console.log(mod.get(obj, "a.b.c.d"));

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