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?

2voto

Chiffie Points 327

J'ai créé un gist pour définir et obtenir des valeurs d'objet en fonction d'une chaîne basée sur la bonne réponse. Vous pouvez le télécharger ou l'utiliser en tant que package npm/yarn.

// yarn add gist:5ceba1081bbf0162b98860b34a511a92
// npm install gist:5ceba1081bbf0162b98860b34a511a92
export const DeepObject = {
  set: setDeep,
  get: getDeep
};

// https://stackoverflow.com/a/6491621
function getDeep(obj: Object, path: string) {
  path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
  path = path.replace(/^\./, '');           // strip a leading dot
  const a = path.split('.');
  for (let i = 0, l = a.length; i < l; ++i) {
    const n = a[i];
    if (n in obj) {
      obj = obj[n];
    } else {
      return;
    }
  }

  return obj;
}

// https://stackoverflow.com/a/18937118
function setDeep(obj: Object, path: string, value: any) {
  let schema = obj;  // a moving reference to internal objects within obj
  const pList = path.split('.');
  const len = pList.length;
  for (let i = 0; i < len - 1; i++) {
    const elem = pList[i];
    if (!schema[elem]) {
      schema[elem] = {};
    }
    schema = schema[elem];
  }

  schema[pList[len - 1]] = value;
}

// Usage
// import {DeepObject} from 'somePath'
//
// const obj = {
//   a: 4,
//   b: {
//     c: {
//       d: 2
//     }
//   }
// };
//
// DeepObject.set(obj, 'b.c.d', 10); // définit obj.b.c.d à 10
// console.log(DeepObject.get(obj, 'b.c.d')); // renvoie 10

2voto

Ashish Dahiya Points 180

En prolongement de la réponse acceptée fournie par @bpmason1, pour prendre en charge les tableaux dans le chemin de la chaîne, par exemple le chemin de la chaîne peut être 'db.mongodb.users[0].name' et 'db.mongodb.users[1].name'.

Cela définira la valeur de la propriété, qui, si elle n'existe pas, sera créée.

var obj = {};

function set(path, value) {
  var schema = obj;
  var keysList = path.split('.');
  var len = keysList.length;
  for (var i = 0; i < len - 1; i++) {
    var key = keysList[i];
    // vérifier si la clé représente un élément de tableau, par exemple users[0]
    if (key.includes('[')) {
      // obtenir le nom de la propriété 'users' à partir de la clé 'users[0]'
      var propertyName = key.substr(0, key.length - key.substr(key.indexOf("["), key.length - key.indexOf("[")).length);
      if (!schema[propertyName]) {
        schema[propertyName] = [];
      }
      // schema['users'][obtenir l'index 0 de 'users[0]']
      if (!schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1))]) {
        // s'il n'existe pas, le créer et l'initialiser
        schema = schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1)]] = {};
      } else {
        schema = schema[propertyName][parseInt(key.substr(key.indexOf("[") + 1, key.indexOf("]") - key.indexOf("[") - 1))];
      }
      continue;
    }
    if (!schema[key]) {
      schema[key] = {};
    }
    schema = schema[key];
  } // loop ends
  // si la dernière clé est un élément de tableau
  if (keysList[len - 1].includes('[')) {
    // obtenir le nom de la propriété 'users' à partir de la clé 'users[0]'
    var propertyName = keysList[len - 1].substr(0, keysList[len - 1].length - keysList[len - 1].substr(keysList[len - 1].indexOf("["), keysList[len - 1].length - keysList[len - 1].indexOf("[")).length);
    if (!schema[propertyName]) {
      schema[propertyName] = [];
    }
    // schema[users][0] = value;
    schema[propertyName][parseInt(keysList[len - 1].substr(keysList[len - 1].indexOf("[") + 1, keysList[len - 1].indexOf("]") - keysList[len - 1].indexOf("[") - 1))] = value;
  } else {
    schema[keysList[len - 1]] = value;
  }
}

// créer si n'existe pas
set("mongo.db.users[0].name.firstname", "hii0");
set("mongo.db.users[1].name.firstname", "hii1");
set("mongo.db.users[2].name", {
  "firstname": "hii2"
});
set("mongo.db.other", "xx");
console.log(obj);

// définira si existe
set("mongo.db.other", "yy");
console.log(obj);

2voto

Sinclair Chen Points 21

Voici une solution utilisant ES 12

function set(obj = {}, key, val) {
  const keys = key.split('.')
  const last = keys.pop()
  keys.reduce((o, k) => o[k] ??= {}, obj)[last] = val
}

(Pour les anciennes versions de JavaScript, vous pouvez utiliser o[k] || o[k] = {} dans le reduce à la place)

Tout d'abord, nous définissons keys comme un tableau contenant tout sauf la dernière clé.

Ensuite, dans le reduce, l'accumulateur va d'un niveau plus profond dans obj à chaque fois, en l'initialisant à un objet vide si la valeur à cette clé n'est pas définie.

Enfin, nous définissons la valeur à la dernière clé à val.

1voto

aggregate1166877 Points 513

Si vous devez uniquement modifier des objets plus profondément imbriqués, une autre méthode pourrait être de référencer l'objet. Comme les objets JS sont manipulés par leurs références, vous pouvez créer une référence vers un objet auquel vous avez accès par clé de chaîne.

Exemple:

// L'objet que nous voulons modifier:
var obj = {
    db: {
        mongodb: {
            host: 'localhost',
            user: 'root'
        }
    },
    foo: {
        bar: baz
    }
};

var key1 = 'mongodb';
var key2 = 'host';

var myRef = obj.db[key1]; // cela crée une référence à obj.db['mongodb']

myRef[key2] = 'ma nouvelle chaîne';

// L'objet ressemble maintenant à ceci:
var obj = {
    db: {
        mongodb: {
            host: 'ma nouvelle chaîne',
            user: 'root'
        }
    },
    foo: {
        bar: baz
    }
};

1voto

ed. Points 863

Une autre approche consiste à utiliser la récursivité pour parcourir l'objet :

(function(root){

  function NestedSetterAndGetter(){
    function setValueByArray(obj, parts, value){

      if(!parts){
        throw 'Aucun tableau de parties passé';
      }

      if(parts.length === 0){
        throw 'Les parties ne devraient jamais avoir une longueur de 0';
      }

      if(parts.length === 1){
        obj[parts[0]] = value;
      } else {
        var next = parts.shift();

        if(!obj[next]){
          obj[next] = {};
        }
        setValueByArray(obj[next], parts, value);
      }
    }

    function getValueByArray(obj, parts, value){

      if(!parts) {
        return null;
      }

      if(parts.length === 1){
        return obj[parts[0]];
      } else {
        var next = parts.shift();

        if(!obj[next]){
          return null;
        }
        return getValueByArray(obj[next], parts, value);
      }
    }

    this.set = function(obj, path, value) {
      setValueByArray(obj, path.split('.'), value);
    };

    this.get = function(obj, path){
      return getValueByArray(obj, path.split('.'));
    };

  }
  root.NestedSetterAndGetter = NestedSetterAndGetter;

})(this);

var setter = new this.NestedSetterAndGetter();

var o = {};
setter.set(o, 'a.b.c', 'pomme');
console.log(o); //=> { a: { b: { c: 'pomme'}}}

var z = { a: { b: { c: { d: 'test' } } } };
setter.set(z, 'a.b.c', {dd: 'zzz'}); 

console.log(JSON.stringify(z)); //=> {"a":{"b":{"c":{"dd":"zzz"}}}}
console.log(JSON.stringify(setter.get(z, 'a.b.c'))); //=> {"dd":"zzz"}
console.log(JSON.stringify(setter.get(z, 'a.b'))); //=> {"c":{"dd":"zzz"}}

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