[javascript] 중첩 된 개체의 동적 설정 속성

레벨 수에 관계없이 기존 속성을 가질 수있는 객체가 있습니다. 예를 들면 :

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) {
    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);
}

제가 만든이 기능은 당신이 필요로하는 것과 조금 더 정확하게 할 수 있습니다.

이 객체에 깊이 중첩 된 대상 값을 변경하고 싶다고 가정 해 보겠습니다.

let myObj = {
    level1: {
        level2: {
           target: 1
       }
    }
}

따라서 함수를 다음과 같이 호출합니다.

setDeep(myObj, ["level1", "level2", "target1"], 3);

결과 :

myObj = {level1 : {level2 : {target : 3}}}

재귀 적으로 설정 플래그를 true로 설정하면 객체가 존재하지 않는 경우 설정됩니다.

setDeep(myObj, ["new", "path", "target"], 3, true);

결과는 다음과 같습니다.

obj = myObj = {
    new: {
         path: {
             target: 3
         }
    },
    level1: {
        level2: {
           target: 3
       }
    }
}


답변

재귀 함수를 사용할 수 있습니다.

/**
 * 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 + 재귀를 사용하여 작은 함수를 작성했습니다.

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');

나는 상태를 업데이트하기 위해 반응에 많이 사용했으며 나에게 꽤 잘 작동했습니다.


답변

ES6에는 Computed Property NameRest 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!"propertyNamelevelTwo


답변

Lodash에는 필요한 작업을 정확히 수행하는 update 라는 메서드 가 있습니다.

이 메소드는 다음 매개 변수를받습니다.

  1. 업데이트 할 개체
  2. 업데이트 할 속성의 경로 (속성은 깊게 중첩 될 수 있음)
  3. 업데이트 할 값을 반환하는 함수 (원래 값을 매개 변수로 제공)

귀하의 예에서는 다음과 같습니다.

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