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?

1voto

John Tyner Points 126

En retard à la fête - voici une fonction vanilla js qui accepte un chemin comme argument et renvoie l'objet/json modifié

let orig_json = {
  string: "Salut",
  number: 0,
  boolean: false,
  object: {
    subString: "Bonjour",
    subNumber: 1,
    subBoolean: true,
    subObject: {
      subSubString: "Bonjour le monde"
    },
    subArray: ["-1", "-2", "-3"]
  },
  array: ["1", "2", "3"]
}

function changeValue(obj_path, value, json) {
  let keys = obj_path.split(".")
  let obj = { ...json },
    tmpobj = {},
    prevobj = {}
  for (let x = keys.length - 1; x >= 0; x--) {
    if (x == 0) {
      obj[keys[0]] = tmpobj
    } else {
      let toeval = 'json.' + keys.slice(0, x).join('.');
      prevobj = { ...tmpobj
      }
      tmpobj = eval(toeval);
      if (x == keys.length - 1) tmpobj[keys[x]] = value
      else {
        tmpobj[keys[x]] = prevobj
      }
    }
  }
  return obj
}

let newjson = changeValue("object.subObject.subSubString", "Au revoir le monde", orig_json);
console.log(newjson)

1voto

Mario Varchmin Points 131

Une autre solution pour ajouter ou remplacer des propriétés :

function propertySetter(property, value) {
  const sampleObject = {
    string: "Salut",
    number: 0,
    boolean: false,
    object: {
      subString: "Bonjour",
      subNumber: 1,
      subBoolean: true,
      subObject: {
        subSubString: "Bonjour le monde",
      },
      subArray: ["-1", "-2", "-3"],
    },
    array: ["1", "2", "3"],
  };

  const keys = property.split(".");
  const propertyName = keys.pop();
  let propertyParent = sampleObject;
  while (keys.length > 0) {
    const key = keys.shift();
    if (!(key in propertyParent)) {
      propertyParent[key] = {};
    }
    propertyParent = propertyParent[key];
  }
  propertyParent[propertyName] = value;
  return sampleObject;
}

console.log(propertySetter("object.subObject.anotherSubString", "Bonjour vous"));

console.log(propertySetter("object.subObject.subSubString", "Bonjour Terre"));

console.log(propertySetter("object.subObject.nextSubString.subSubSubString", "Bonjourrr"));

1voto

Jeff Walters Points 4343

Inspiré par la méthode setIn d'ImmutableJS qui ne mutera jamais l'original. Cela fonctionne avec des valeurs imbriquées mixtes dans un tableau et un objet.

function setIn(obj = {}, [prop, ...rest], value) {
    const newObj = Array.isArray(obj) ? [...obj] : {...obj};
    newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
    return newObj;
}

var obj = {
  a: {
    b: {
      c: [
        {d: 5}
      ]
    }
  }
};

const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");

//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}

1voto

Masoud Aghaei Points 358

Comme l'a dit @aheuermann, vous pouvez utiliser set de la bibliothèque lodash,

Cependant, si vous ne voulez pas ajouter lodash à votre projet pour une raison quelconque, vous pouvez utiliser une fonction récursive qui définit/remplace une valeur dans un objet.

/**
 * fonction récursive appelée dans la fonction principale 
 * @param obj JSON initial
 * @param keysList tableau de clés
 * @param valeur valeur que vous souhaitez définir
 * @returns JSON final
 */
function recursionSet(obj, keysList, value) {
    const clé = keysList[0]
    if (keysList.length === 1) return { ...obj, [clé]: value }
    return { ...obj, [clé]: (recursionSet(obj?.[clé] || {}, keysList.slice(1), value)) }
}

/**
 * fonction principale que vous pouvez appeler pour définir une valeur dans un objet par des clés imbriquées
 * @param obj JSON initial
 * @param keysString clés imbriquées séparées par "."
 * @param valeur valeur que vous souhaitez définir
 * @returns JSON final
 */
function objectSet(obj, keysString, valeur) {
    return recursionSet(obj, keysString.split('.'), valeur)
}

// utilisation simple
const a1 = {}
console.log('utilisation simple :', objectSet(a1, "b.c.d", 5))

// conserver les données initiales
const a2 = {b:{e: 8}}
console.log('conserver les données initiales :', objectSet(a2, "b.c.d", 5))

// remplacer des données
const a3 = {b:{e: 8, c:2}}
console.log('remplacer des données :', objectSet(a3, "b.c.d", 5))

// valeur complexe
const a4 = {b:{e: 8, c:2}}
console.log('valeur complexe :', objectSet(a4, "b.c.d", {f:12}))

0voto

C Smith Points 162

Si vous souhaitez une fonction qui nécessite que les propriétés précédentes existent, alors vous pourriez utiliser quelque chose comme ceci, cela renverrait également un indicateur indiquant si elle a réussi à trouver et définir la propriété imbriquée.

function set(obj, path, value) {
    var parts = (path || '').split('.');
    // en utilisant 'every' afin que nous puissions renvoyer un indicateur indiquant si nous avons réussi à définir la valeur.
    return parts.every((p, i) => {
        if (!obj) return false; // annuler tôt car nous n'avons pas trouvé de propriété imbriquée.
        if (i === parts.length - 1){ // nous sommes à la dernière partie du chemin.
            obj[parts[i]] = value;          
        }else{
            obj = obj[parts[i]]; // écraser la référence des fonctions de l'objet avec celui imbriqué.            
        }   
        return true;        
    });
}

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