중첩 된 개체의 동적 설정 속성
레벨 수에 관계없이 기존 속성을 가질 수있는 객체가 있습니다. 예를 들면 :
var obj = {
db: {
mongodb: {
host: 'localhost'
}
}
};
이에 대해 다음과 같이 속성을 설정 (또는 덮어 쓰기)하고 싶습니다.
set('db.mongodb.user', 'root');
// or:
set('foo.bar', 'baz');
속성 문자열은 깊이를 가질 수 있고 값은 모든 유형 / 사물 일 수 있습니다.
속성 키가 이미 존재하는 경우 값으로서의 객체 및 배열을 병합 할 필요가 없습니다.
이전 예제는 다음 객체를 생성합니다.
var obj = {
db: {
mongodb: {
host: 'localhost',
user: 'root'
}
},
foo: {
bar: baz
}
};
그러한 기능을 어떻게 실현할 수 있습니까?
이 함수는 지정한 인수를 사용하여 obj
컨테이너 의 데이터를 추가 / 업데이트해야합니다 . obj
스키마 의 어떤 요소 가 컨테이너이고 값 (문자열, 정수 등)인지 추적해야합니다 . 그렇지 않으면 예외가 발생하기 시작합니다.
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');
Lodash에는 _.set () 메서드가 있습니다.
_.set(obj, 'db.mongodb.user', 'root');
_.set(obj, 'foo.bar', 'baz');
조금 늦었지만 여기에 도서관이 아닌 간단한 대답이 있습니다.
/**
* 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) {
let level = 0;
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;
} else {
return a[b];
}
}, obj);
}
제가 만든이 기능은 당신이 필요로하는 것을 정확히 할 수 있습니다.
이 객체에 깊이 중첩 된 대상 값을 변경하고 싶다고 가정 해 보겠습니다.
let myObj = {
level1: {
level2: {
target: 1
}
}
}
따라서 함수를 다음과 같이 호출합니다.
setDeep(myObj, ["level1", "level2", "target1"], 3);
결과 :
myObj = {level1 : {level2 : {target : 3}}}
재귀 적으로 설정 플래그를 true로 설정하면 객체가 존재하지 않는 경우 설정됩니다.
setDeep(myObj, ["new", "path", "target"], 3);
결과는 다음과 같습니다.
obj = myObj = {
new: {
path: {
target: 3
}
},
level1: {
level2: {
target: 3
}
}
}
Lodash에는 필요한 작업을 정확히 수행하는 update 라는 메서드 가 있습니다.
이 메소드는 다음 매개 변수를받습니다.
- 업데이트 할 개체
- 업데이트 할 속성의 경로 (속성은 깊게 중첩 될 수 있음)
- 업데이트 할 값을 반환하는 함수 (원래 값을 매개 변수로 제공)
귀하의 예에서는 다음과 같습니다.
_.update(obj, 'db.mongodb.user', function(originalValue) {
return 'root'
})
@ 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;
}
예:
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' } }
재귀 함수를 사용할 수 있습니다.
/**
* 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)
}
더 간단합니다!
ES6에는 Computed Property Name 및 Rest Parameter를 사용하여이 작업을 수행하는 매우 멋진 방법이 있습니다 .
const obj = {
levelOne: {
levelTwo: {
levelThree: "Set this one!"
}
}
}
const updatedObj = {
...obj,
levelOne: {
...obj.levelOne,
levelTwo: {
...obj.levelOne.levelTwo,
levelThree: "I am now updated!"
}
}
}
에서 levelThree
속성을 설정하는 동적 속성 인 경우 에서 속성 의 이름을 보유하는 위치 levelTwo
를 사용할 수 있습니다 .[propertyName]: "I am now updated!"
propertyName
levelTwo
목표를 달성하기 위해 ES6 + 재귀를 사용하여 작은 함수를 작성했습니다.
updateObjProp = (obj, value, propPath) => {
const [head, ...rest] = propPath.split('.');
!rest.length
? obj[head] = value
: this.updateObjProp(obj[head], value, rest);
}
const user = {profile: {name: 'foo'}};
updateObjProp(user, 'fooChanged', 'profile.name');
나는 상태를 업데이트하기 위해 반응에 많이 사용했으며 나에게 꽤 잘 작동했습니다.
더 깊은 중첩 된 개체 만 변경해야하는 경우 다른 방법은 개체를 참조하는 것입니다. JS 객체는 해당 참조로 처리되므로 문자열 키 액세스 권한이있는 객체에 대한 참조를 만들 수 있습니다.
예:
// 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
}
};
또 다른 접근법은 재귀를 사용하여 객체를 파헤치는 것입니다.
(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"}}
정답을 기반으로 문자열별로 obj 값을 설정하고 얻는 요점 을 만들었습니다 . 다운로드하거나 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); // sets obj.b.c.d to 10
// console.log(DeepObject.get(obj, 'b.c.d')); // returns 10
나는 똑같은 것을 달성해야했지만 Node.js에서 ... 그래서이 멋진 모듈을 찾았습니다 : https://www.npmjs.com/package/nested-property
예:
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"));
이전 속성이 필요한 함수가 존재하기를 원한다면 이와 같은 것을 사용할 수 있으며 중첩 된 속성을 찾아서 설정할 수 있는지 여부를 나타내는 플래그도 반환합니다.
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;
});
}
JQuery에는 확장 메서드가 있습니다.
https://api.jquery.com/jquery.extend/
덮어 쓰기를 객체로 전달하면 둘을 병합합니다.
ClojureScript assoc-in
( https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L5280 )에서 영감을 받아 재귀를 사용합니다.
/**
* 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);
}
노트 :
With the provided implementation,
assoc_in({"a": 1}, ["a", "b"], 2)
returns
{"a": 1}
I would prefer that it throw an error in this case. If desired, you can add a check in assoc
to verify m
is either an object or array and throw an error otherwise.
참고URL : https://stackoverflow.com/questions/18936915/dynamically-set-property-of-nested-object
'Development Tip' 카테고리의 다른 글
document.getElementById를 사용하여 iframe 내부의 요소를 선택하는 방법 (0) | 2020.12.06 |
---|---|
TypeScript에서 정적 메서드에 액세스하는 방법 (0) | 2020.12.06 |
Google 결과에서 데이터를 긁어도 괜찮습니까? (0) | 2020.12.06 |
Swift 코드 실행을 벤치마킹하는 방법은 무엇입니까? (0) | 2020.12.06 |
클릭시 Vue.js 토글 클래스 (0) | 2020.12.06 |