ProgramingTip

자바 스크립트 : 배열로 주어진 객체 이름을 사용하여 중첩 된 객체를 동적으로 만드는 방법

bestdevel 2020. 12. 31. 23:33
반응형

자바 스크립트 : 배열로 주어진 객체 이름을 사용하여 중첩 된 객체를 동적으로 만드는 방법


누군가이 Javascript로 나를 도울 수 있기를 바랍니다.

"설정"이라는 개체가 있고 해당 개체에 새 설정을 추가하는 함수를 작성하고 싶습니다.

새 설정의 이름과 값이 문자열로 제공됩니다. 설정의 이름을 제공하는 문자열은 밑줄로 배열로 나뉩니다. 새 설정은 설정 값을 제공하는 문자열이어야하는 마지막 부분을 제외하고 배열의 각 부분에서 지정한 이름으로 새 중첩 개체를 만들어 기존 "설정"개체에 추가해야합니다. 그런 다음 설정을 참조하고 값을 경고 할 수 있어야합니다. 이렇게 정적 인 방식으로 할 수 있습니다 ...

var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");

Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;

alert(Settings.Modules.Mediaplayers.Video.Plugin);

... 중첩 된 객체를 생성하는 부분이이 작업을 수행합니다.

Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";

그러나 설정 이름을 구성하는 부분의 수가 다를 수 있습니다. 예를 들어 newSettingName은 "Modules_Floorplan_Image_Src"일 수 있으므로 다음과 같은 함수를 사용하여 동적으로 수행하고 싶습니다.

createSetting (newSettingNameArray, newSettingValue);

function createSetting(setting, value) {
    // code to create new setting goes here
}

누구든지 동적으로 수행하는 방법을 알아낼 수 있습니까?

배열을 지체하기 위해 for ... 루프가 있어야한다고 생각하지만 중첩 된 객체를 만드는 방법을 알아낼 수 없었습니다.

여기까지왔다면 어쩔 수 없더라도 시간을내어 읽어 주셔서 대단히 감사합니다.


function assign(obj, keyPath, value) {
   lastKeyIndex = keyPath.length-1;
   for (var i = 0; i < lastKeyIndex; ++ i) {
     key = keyPath[i];
     if (!(key in obj)){
       obj[key] = {}
     }
     obj = obj[key];
   }
   obj[keyPath[lastKeyIndex]] = value;
}

용법:

var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');

짧고 빠른 함수를 넣으십시오 (재귀 없음).

var createNestedObject = function( base, names ) {
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }
};

// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.

이미 존재하는 계층 구조 부분을 건너 뜁니다. 계층이 이미 생성되었는지 확실하지 않은 경우 유용합니다.

또는:

계층 구조의 마지막 개체에 값을 직접 할당 할 수 있고 마지막 개체를 반환하므로 함수 호출을 연결할 수있는 더 멋진 버전입니다.

// Function: createNestedObject( base, names[, value] )
//   base: the object on which to create the hierarchy
//   names: an array of strings contaning the names of the objects
//   value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
    // If a value is given, remove the last name and keep it for later:
    var lastName = arguments.length === 3 ? names.pop() : false;

    // Walk the hierarchy, creating new objects where needed.
    // If the lastName was removed, then the last object is not set yet:
    for( var i = 0; i < names.length; i++ ) {
        base = base[ names[i] ] = base[ names[i] ] || {};
    }

    // If a value was given, set it to the last name:
    if( lastName ) base = base[ lastName ] = value;

    // Return the last object in the hierarchy:
    return base;
};

// Usages:

createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.

var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300

createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400

참고 : 계층 구조가 표준 객체가 아닌 다른 값으로 구축되어야하는 경우 {}아래 TimDog의 답변을 참조하십시오.

편집 : 루프 대신 일반 루프를 사용합니다 for...in. 라이브러리가 Array 프로토 타입을 수정하는 경우 더 안전합니다.


내 ES2015 솔루션. 기존 값을 유지합니다.

const set = (obj, path, val) => { 
    const keys = path.split('.');
    const lastKey = keys.pop();
    const lastObj = keys.reduce((obj, key) => 
        obj[key] = obj[key] || {}, 
        obj); 
    lastObj[lastKey] = val;
};

예:

const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}

또 다른 재귀 솔루션 :

var nest = function(obj, keys, v) {
    if (keys.length === 1) {
      obj[keys[0]] = v;
    } else {
      var key = keys.shift();
      obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
    }

    return obj;
};

사용 예 :

var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}

ES6 사용이 단축됩니다. 경로를 배열로 설정하십시오. 먼저 객체를 채우기 위해 배열을 반대로해야합니다.

let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();

const nestedObject = obj.reduce((prev, current) => (
    {[current]:{...prev}}
), {});

다음은 중첩 계층 구조의 각 요소에 고유 한 값을 설정할 수있는 jlgrall의 답변에 대한 간단한 조정입니다 .

var createNestedObject = function( base, names, values ) {
    for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};

도움이 되었기를 바랍니다.


다음은 중첩 된 개체를 동적으로 만드는 기능적 솔루션입니다.

const nest = (path, obj) => {
  const reversedPath = path.split('.').reverse();

  const iter = ([head, ...tail], obj) => {
    if (!head) {
      return obj;
    }
    const newObj = {[head]: {...obj}};
    return iter(tail, newObj);
  }
  return iter(reversedPath, obj);
}

예:

const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}

이 질문은 매우 오래되었습니다. 그러나 노드에서 이와 같은 작업을 수행해야 할 필요성을 발견 한 후 모듈을 만들어 npm에 게시했습니다. Nestob

var nestob = require('nestob');

//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();

//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});

console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
  orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }

//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome

//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols

//You can also use nestob to modify objects not created using nestob
var normalObj = {};

nestob.setNested(normalObj, 'running.out.of', 'words');

console.log(normalObj);
//{ running: { out: { of: 'words' } } }

console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false

중첩 필드에 특정 값을 설정하는이 ES6 불변 방법을 좋아합니다.

const setValueToField = (fields, value) => {
  const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
  return fields.reduceRight(reducer, {});
};

그런 다음 대상 개체를 만드는 데 사용하십시오.

const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }

재귀 함수를 사용해보십시오.

function createSetting(setting, value, index) {
  if (typeof index !== 'number') {
    index = 0;
  }

  if (index+1 == setting.length ) {
    settings[setting[index]] = value;
  }
  else {
    settings[setting[index]] = {};
    createSetting(setting, value, ++index);
  }
}

나는 이것이 더 짧다고 생각합니다.

Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");

a = Settings;
for (var i = 0 in newSettingNameArray) {
    var x = newSettingNameArray[i];
    a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
    a = a[x];
}

@jlgrall의 답변이 훌륭했지만 단순화 한 후 Chrome에서 작동하지 않았습니다. 누구나 라이트 버전을 원하는 경우 내 수정 사항은 다음과 같습니다.

var callback = 'fn.item1.item2.callbackfunction',
    cb = callback.split('.'),
    baseObj = window;

function createNestedObject(base, items){
    $.each(items, function(i, v){
        base = base[v] = (base[v] || {});
    });
}

callbackFunction = createNestedObject(baseObj, cb);

console.log(callbackFunction);

유용하고 관련이 있기를 바랍니다. 죄송합니다. 방금이 예제를 깨뜨 렸습니다 ...


자신 만의 Object 메서드를 정의 할 수 있습니다. 또한 간결함을 위해 밑줄을 사용하고 있습니다.

var _ = require('underscore');

// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
    if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
    var tmpo = this;
    while (i = addr.shift())
        tmpo = tmpo[i];
    return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
    if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
    this.pick(_.initial(addr))[_.last(addr)] = val;
};

샘플 사용법 :

var obj = { 
           'foo': {
                   'bar': 0 }}

obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}

A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.

var updateStateQuery = function(state, path, value) {
    var names = path.split('.');
    for (var i = 0, len = names.length; i < len; i++) {
        if (i == (len - 1)) {
            state = state[names[i]] = state[names[i]] || value;
        }
        else if (parseInt(names[i+1]) >= 0) {
            state = state[names[i]] = state[names[i]] || [];
        }
        else {
            state = state[names[i]] = state[names[i]] || {};
        }
    }
};

Set Nested Data:

function setNestedData(root, path, value) {
  var paths = path.split('.');
  var last_index = paths.length - 1;
  paths.forEach(function(key, index) {
    if (!(key in root)) root[key] = {};
    if (index==last_index) root[key] = value;
    root = root[key];
  });
  return root;
}

var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}

Get Nested Data:

function getNestedData(obj, path) {
  var index = function(obj, i) { return obj && obj[i]; };
  return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined

Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.

 function buildDir(obj, path){
   var paths = path.split('_');
   var final = paths.pop();
   for (let i = 1; i <= paths.length; i++) {
     var key = "obj['" + paths.slice(0, i).join("']['") + "']"
     console.log(key)
     eval(`${key} = {}`)
   }
   eval(`${key} = '${final}'`)
   return obj
 }

 var newSettingName = "Modules_Video_Plugin_JWPlayer";
 var Settings = buildDir( {}, newSettingName );

Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;


Try this: https://github.com/silkyland/object-to-formdata

var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
             a:1,
             b:[
                {c: 3},
                {d: 4}
             ]
})

Result :

fd = [
       a => 1,
       b => [
         c => 3,
         d => 4
       ]
]

ReferenceURL : https://stackoverflow.com/questions/5484673/javascript-how-to-dynamically-create-nested-objects-using-object-names-given-by

반응형