Кодирование значения ключа объекта Javascript. Динамическая установка вложенного значения - PullRequest
2 голосов
/ 14 января 2010

Я работаю над небольшой библиотекой, которая позволяет мне выполнять базовое кодирование значения ключа с объектами. Скажем, у меня есть следующий объект:

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };

И у меня есть следующая функция JavaScript:

var setData = function(path, value) {
    eval("data." + path + "= value;");
};

И используется:

setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99

Это работает, однако я бы хотел выполнить вышеупомянутое без использования eval. Это возможно, или eval лучший путь?

EDIT:

ПРИМЕЧАНИЕ Можно предположить, что значение, которое вы устанавливаете, существует, по крайней мере, для глубины пути - 1. Меня больше беспокоит установка значения существующего объекта.

Ответы [ 5 ]

17 голосов
/ 14 января 2010

Рекурсия - это то, что вам нужно:

function setData(key,val,obj) {
  if (!obj) obj = data; //outside (non-recursive) call, use "data" as our base object
  var ka = key.split(/\./); //split the key by the dots
  if (ka.length < 2) { 
    obj[ka[0]] = val; //only one part (no dots) in key, just set value
  } else {
    if (!obj[ka[0]]) obj[ka[0]] = {}; //create our "new" base obj if it doesn't exist
    obj = obj[ka.shift()]; //remove the new "base" obj from string array, and hold actual object for recursive call
    setData(ka.join("."),val,obj); //join the remaining parts back up with dots, and recursively set data on our new "base" obj
  }    
}

setData("key1", "updated value1"); // data.key1 == "updated value1"
setData("key2.nested1", 99); // data.key2.nested1 == 99
4 голосов
/ 14 января 2010

Да, это легко. obj.prop = val совпадает с obj['prop'] = val, поэтому вы можете переписать setData следующим образом:

var setData = function (path, value) {
    data[path] = value;
};

И это должно сделать.

Тем не менее, я не уверен, какой успех вы будете иметь с 3-м уровнем (если это имеет смысл.) Obj.prop.prop не может быть указан obj ['prop.prop'] и вместо этого придется на него должна ссылаться obj ['prop'] ['prop'], поэтому setData придется переписать, чтобы учесть это. Мне не очень повезло с этим, хотя.

Редактировать: Я сделал один, который (для меня) устанавливает его как вы хотите, но не дальше. К сожалению, если вы вкладываете глубже, чем ваши примеры, то я не вижу реальной причины отказаться от eval. Я не делал никаких тестов, но в какой-то момент вычисления будут дороже в вычислительном отношении, чем даже eval (что довольно сложно выполнить).

Это то, что я придумал, который установит их по крайней мере на 1 уровень; то есть он будет работать с key2.nested1, но не с key2.nested1.i_love_nesting, если это имеет смысл:

var setData = function (path, value) {
    if (path.indexOf('.') != -1) {
        path = path.split('.');
        for (var i = 0, l = path.length; i < l; i++) {
            if (typeof(data[path[i]]) === 'object') {
                continue;
            } else {
                data[path[i - 1]][path[i]] = value;
            }
        }
    } else {
        data[path] = value;
    }
};

Надеюсь, это поможет. Я мог бы написать это не самым эффективным способом, хотя ...

1 голос
/ 30 декабря 2016

Вдохновленный @ user1416920 Я сделал это:

function setData(key,val,obj)
{
    keys = key.split(/\./);
    last = keys.pop();

    keys.forEach(function(key)
    {
      if(typeof obj[key] === "undefined")
        obj[key] = {}; 
      obj = obj[key]; 
    });

    obj[last] = val;
}

То, что он делает, говорит само за себя ^^.

0 голосов
/ 26 октября 2013

Я думаю, что эту проблему станет проще и естественнее решить, если вы будете гибки в определении своего пути. Если вместо того, чтобы делать что-то вроде key2.nested1, вы можете сделать {key2:{nested1:{}}} (обозначение объекта), это становится довольно тривиальным:

function setData(subtree, path){
    var nodeName = Object.keys(path);

    if(typeof path[nodeName] == 'object')
        setData(subtree[nodeName], path[nodeName]);
    else
        subtree[nodeName] = path[nodeName];
}

var data = { key1: "value1", key2: { nested1: 1, nested2: "wowza!" } };

// For the initial call, 'subtree' is the full data tree
setData(data, {key2:{nested1:99}});

Вы просто возвращаетесь ко второму аргументу вызова setData, пока не найдете значение. Каждый раз, когда вы выполняете рекурсию, вы переходите в дерево data в указанной точке.

Эту функцию можно легко изменить, чтобы она также принимала указанную вами точечную нотацию, но для меня это более чистый и более естественный подход (плюс, операции с строками медленные).

Приветствия

0 голосов
/ 25 мая 2012
function setData(key,val,obj){
    keys = key.split(/\./);
    to_set = keys.pop();
    keys.forEach(function(obj_name){ obj = obj[obj_name]; });
    obj[to_set] = val;
}
...