Ich habe ein Objekt, das eine beliebige Anzahl von Ebenen tief sein kann und vorhandene Eigenschaften haben kann. Zum Beispiel:
var obj = {
db: {
mongodb: {
host: 'localhost'
}
}
};
Darauf möchte ich Eigenschaften wie folgt setzen (oder überschreiben):
set('db.mongodb.user', 'root');
// or:
set('foo.bar', 'baz');
Wobei die Eigenschaftszeichenfolge eine beliebige Tiefe haben kann und der Wert ein beliebiger Typ / Gegenstand sein kann.
Objekte und Arrays als Werte müssen nicht zusammengeführt werden, falls der Eigenschaftsschlüssel bereits vorhanden ist.
Das vorherige Beispiel würde folgendes Objekt erzeugen:
var obj = {
db: {
mongodb: {
host: 'localhost',
user: 'root'
}
},
foo: {
bar: baz
}
};
Wie kann ich eine solche Funktion realisieren?
javascript
ecmascript-5
John B.
quelle
quelle
set('foo', 'bar'); set('foo.baz', 'qux');
, wofoo
zuerst einString
dann ein wirdObject
? Was passiert mit'bar'
?set()
Methode entfernen und nurobj.db.mongodb.user = 'root';
genau das haben, was Sie zu wollen scheinen?bar
wird von der überschriebenObject
. @adeneo und @rmertins In der Tat :) Aber ich muss leider eine andere Logik umwickeln. @ Robert Levy Ich fand das und bekam den Zugriff zum Laufen, aber das Einstellen scheint so viel komplizierter ...Antworten:
Diese Funktion sollte unter Verwendung der von Ihnen angegebenen Argumente die Daten im
obj
Container hinzufügen / aktualisieren . Beachten Sie, dass Sieobj
nachverfolgen müssen, welche Elemente im Schema Container und welche Werte (Zeichenfolgen, Ints usw.) sind. Andernfalls werden Ausnahmen ausgelöst.obj = {}; // global object function set(path, value) { var schema = obj; // a moving reference to internal objects within obj var pList = path.split('.'); var len = pList.length; for(var i = 0; i < len-1; i++) { var elem = pList[i]; if( !schema[elem] ) schema[elem] = {} schema = schema[elem]; } schema[pList[len-1]] = value; } set('mongo.db.user', 'root');
quelle
var schema = obj
stattobj
überall verwendet hast ?schema
ist ein Zeiger, mit dem der Pfad entlang bewegt wirdschema = schema[elem]
.schema[pList[len - 1]]
Zeigt also nach der for-Schleife auf mongo.db.user inobj
.cloneDeep
Funktion nutzen.Lodash hat eine _.set () -Methode.
_.set(obj, 'db.mongodb.user', 'root'); _.set(obj, 'foo.bar', 'baz');
quelle
Ein bisschen spät, aber hier ist eine nicht bibliothekarische, einfachere Antwort:
/** * Dynamically sets a deeply nested value in an object. * Optionally "bores" a path to it if its undefined. * @function * @param {!object} obj - The object which contains the value you want to change/set. * @param {!array} path - The array representation of path to the value you want to change/set. * @param {!mixed} value - The value you want to set it to. * @param {boolean} setrecursively - If true, will set value of non-existing path as well. */ function setDeep(obj, path, value, setrecursively = false) { path.reduce((a, b, level) => { if (setrecursively && typeof a[b] === "undefined" && level !== path.length){ a[b] = {}; return a[b]; } if (level === path.length){ a[b] = value; return value; } return a[b]; }, obj); }
Diese Funktion, die ich gemacht habe, kann genau das tun, was Sie brauchen und ein bisschen mehr.
Angenommen, wir möchten den Zielwert ändern, der tief in diesem Objekt verschachtelt ist:
let myObj = { level1: { level2: { target: 1 } } }
Also würden wir unsere Funktion so nennen:
setDeep(myObj, ["level1", "level2", "target1"], 3);
wird darin enden, dass:
myObj = {level1: {level2: {target: 3}}}
Wenn Sie das Set-Flag rekursiv auf true setzen, werden Objekte festgelegt, wenn sie nicht vorhanden sind.
setDeep(myObj, ["new", "path", "target"], 3, true);
wird dazu führen:
obj = myObj = { new: { path: { target: 3 } }, level1: { level2: { target: 3 } } }
quelle
level
ich dasreduce
dritte Argument verwendet.level
muss +1 oderpath.length
-1 seinWir können eine Rekursionsfunktion verwenden:
/** * Sets a value of nested key string descriptor inside a Object. * It changes the passed object. * Ex: * let obj = {a: {b:{c:'initial'}}} * setNestedKey(obj, ['a', 'b', 'c'], 'changed-value') * assert(obj === {a: {b:{c:'changed-value'}}}) * * @param {[Object]} obj Object to set the nested key * @param {[Array]} path An array to describe the path(Ex: ['a', 'b', 'c']) * @param {[Object]} value Any value */ export const setNestedKey = (obj, path, value) => { if (path.length === 1) { obj[path] = value return } return setNestedKey(obj[path[0]], path.slice(1), value) }
Es ist einfacher!
quelle
obj[path[0]] = value;
weil siepath
immer vom Typ iststring[]
, auch wenn nur noch 1 Zeichenfolge übrig ist.obj[['a']] = 'new value'
. Überprüfen Sie den Code: jsfiddle.net/upsdne03Ich schreibe nur eine kleine Funktion mit ES6 + Rekursion, um das Ziel zu erreichen.
updateObjProp = (obj, value, propPath) => { const [head, ...rest] = propPath.split('.'); !rest.length ? obj[head] = value : this.updateObjProp(obj[head], value, rest.join('.')); } const user = {profile: {name: 'foo'}}; updateObjProp(user, 'fooChanged', 'profile.name');
Ich habe es oft benutzt, um auf den Update-Status zu reagieren. Es hat ziemlich gut für mich funktioniert.
quelle
this.updateStateProp(obj[head], value, rest);
sollte seinthis.updateStateProp(obj[head], value, rest.join());
ES6 hat eine ziemlich coole Möglichkeit, dies auch mit dem Namen der berechneten Eigenschaften und dem Rest-Parameter zu tun .
const obj = { levelOne: { levelTwo: { levelThree: "Set this one!" } } } const updatedObj = { ...obj, levelOne: { ...obj.levelOne, levelTwo: { ...obj.levelOne.levelTwo, levelThree: "I am now updated!" } } }
Wenn
levelThree
es sich um eine dynamische Eigenschaft handelt, dh zum Festlegen einer EigenschaftlevelTwo
, können Sie verwenden,[propertyName]: "I am now updated!"
wopropertyName
der Name der Eigenschaft enthalten istlevelTwo
.quelle
Lodash hat eine Methode namens Update , die genau das tut, was Sie brauchen.
Diese Methode empfängt die folgenden Parameter:
In Ihrem Beispiel würde es so aussehen:
_.update(obj, 'db.mongodb.user', function(originalValue) { return 'root' })
quelle
Inspiriert von der Antwort von @ bpmason1:
function 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; }
Beispiel:
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' } }
quelle
Ich habe das Wesentliche erstellt , um obj-Werte anhand einer Zeichenfolge basierend auf der richtigen Antwort festzulegen und abzurufen. Sie können es herunterladen oder als npm / yarn-Paket verwenden.
// 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); // sets obj.b.c.d to 10 // console.log(DeepObject.get(obj, 'b.c.d')); // returns 10
quelle
Wenn Sie nur tiefere verschachtelte Objekte ändern müssen, besteht eine andere Methode darin, auf das Objekt zu verweisen. Da JS-Objekte von ihren Referenzen behandelt werden, können Sie eine Referenz auf ein Objekt erstellen, auf das Sie Zugriff auf einen Zeichenfolgenschlüssel haben.
Beispiel:
// The object we want to modify: var obj = { db: { mongodb: { host: 'localhost', user: 'root' } }, foo: { bar: baz } }; var key1 = 'mongodb'; var key2 = 'host'; var myRef = obj.db[key1]; //this creates a reference to obj.db['mongodb'] myRef[key2] = 'my new string'; // The object now looks like: var obj = { db: { mongodb: { host: 'my new string', user: 'root' } }, foo: { bar: baz } };
quelle
Ein anderer Ansatz besteht darin, die Rekursion zu verwenden, um durch das Objekt zu graben:
(function(root){ function NestedSetterAndGetter(){ function setValueByArray(obj, parts, value){ if(!parts){ throw 'No parts array passed in'; } if(parts.length === 0){ throw 'parts should never have a length of 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', 'apple'); console.log(o); //=> { a: { b: { c: 'apple'}}} 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"}}
quelle
Ich musste dasselbe erreichen, aber in Node.js ... Also fand ich dieses schöne Modul: https://www.npmjs.com/package/nested-property
Beispiel:
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"));
quelle
Ich habe meine eigene Lösung mit reinem es6 und Rekursion entwickelt, die das ursprüngliche Objekt nicht mutiert.
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);
quelle
Wenn Sie möchten, dass eine Funktion vorhanden ist, für die vorherige Eigenschaften erforderlich sind, können Sie Folgendes verwenden. Außerdem wird ein Flag zurückgegeben, das angibt, ob es gelungen ist, die verschachtelte Eigenschaft zu finden und festzulegen.
function set(obj, path, value) { var parts = (path || '').split('.'); // using 'every' so we can return a flag stating whether we managed to set the value. return parts.every((p, i) => { if (!obj) return false; // cancel early as we havent found a nested prop. if (i === parts.length - 1){ // we're at the final part of the path. obj[parts[i]] = value; }else{ obj = obj[parts[i]]; // overwrite the functions reference of the object with the nested one. } return true; }); }
quelle
Inspiriert von ClojureScript
assoc-in
( https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L5280 ) unter Verwendung der Rekursion:/** * Associate value (v) in object/array (m) at key/index (k). * If m is falsy, use new object. * Returns the updated object/array. */ function assoc(m, k, v) { m = (m || {}); m[k] = v; return m; } /** * Associate value (v) in nested object/array (m) using sequence of keys (ks) * to identify the path to the nested key/index. * If one of the values in the nested object/array doesn't exist, it adds * a new object. */ function assoc_in(m={}, [k, ...ks], v) { return ks.length ? assoc(m, k, assoc_in(m[k], ks, v)) : assoc(m, k, v); } /** * Associate value (v) in nested object/array (m) using key string notation (s) * (e.g. "k1.k2"). */ function set(m, s, v) { ks = s.split("."); return assoc_in(m, ks, v); }
Hinweis:
Mit der bereitgestellten Implementierung
assoc_in({"a": 1}, ["a", "b"], 2)
kehrt zurück
{"a": 1}
Ich würde es vorziehen, dass es in diesem Fall einen Fehler auslöst. Falls gewünscht, können Sie einen Check-in hinzufügen, um
assoc
zu überprüfen, obm
es sich entweder um ein Objekt oder ein Array handelt, und andernfalls einen Fehler auslösen.quelle
Ich habe versucht, diese Set-Methode kurz zu schreiben , sie kann jemandem helfen!
function set(obj, key, value) { let keys = key.split('.'); if(keys.length<2){ obj[key] = value; return obj; } let lastKey = keys.pop(); let fun = `obj.${keys.join('.')} = {${lastKey}: '${value}'};`; return new Function(fun)(); } var obj = { "hello": { "world": "test" } }; set(obj, "hello.world", 'test updated'); console.log(obj); set(obj, "hello.world.again", 'hello again'); console.log(obj); set(obj, "hello.world.again.onece_again", 'hello once again'); console.log(obj);
quelle
JQuery hat eine Extend-Methode:
https://api.jquery.com/jquery.extend/
Übergeben Sie einfach die Überschreibungen als Objekt und es werden die beiden zusammengeführt.
quelle