在可变深度下更新对象值的更好方法

时间:2018-06-22 14:39:38

标签: javascript object local-storage

我正在开发一些使用处理程序在localStorage中读取/写入信息的软件。您可以在这里找到一个有效的示例:http://jsbin.com/wifucugoko/edit?js,console

我的问题在于下面的代码段(关注switch语句):

_t.set = function(path, value) { // Update a single value or object
        if (~path.indexOf(".")) {
            let o = path.split(".")[0],
                p = this.get(o),
                q = path.split(".").slice(1);
            switch (q.length) { 
            // There has to be a better way to do this... 
                case 1:
                    p[q[0]] = value; 
                    break;
                case 2:
                    p[q[0]][q[1]] = value; 
                    break;
                case 3:
                    p[q[0]][q[1]][q[2]] = value; 
                    break;
                case 4:
                    p[q[0]][q[1]][q[2]][q[3]] = value; 
                    break;
                case 5:
                    p[q[0]][q[1]][q[2]][q[3]][q[4]] = value; 
                    break;
                case 6: 
                    p[q[0]][q[1]][q[2]][q[3]][q[4]][q[5]] = value; 
                    break;
                default:  
                    return "error";
            }  
            b.setItem(o, JSON.stringify(p));
            return p;
        } else {
           b.setItem(path, JSON.stringify(value));
            return this.get(path);
        }
    };

我不会成为使用此代码库的唯一人,并且我正在尝试使其他人更容易更新可以放置在localStorage中的任何值。现在,您可以使用local.set('item.subitem.proeprty', 'value')之类的值来更新值。尽管上面的代码可以做到这一点,但它很丑陋且无法扩展

如何改进此方法,以(1)自动更新嵌套在任意深度的属性,而不是编写无限长的switch语句,以及(2)不在父对象上加上{ {1}}值更新后?


该问题与我对[object Object] 的使用无关。我最初将此问题发布在代码审查中,这需要一个有效的上下文示例。他们立即解决了这个问题,因为部分问题是,一旦您开始处理嵌套超过六个对象的值,我提供的代码将无法正常工作。尽管我可以无限期地继续我的switch语句,但这正是我要避免的

在提供了三个示例后,您将看到在一个位置设置值不会删除其他位置的值:

localStorage

所有这些值,尽管是在不同的时间设置的,但只能更新或创建一个值,而不会清除其他任何先前存在的值。

6 个答案:

答案 0 :(得分:1)

对我来说,最小优化如下:

    if (~path.indexOf(".")) {
        let o = path.split(".")[0],
            p = this.get(o),
            q = path.split(".").slice(1),
            dist = p;
            q.forEach(function(item, index) {
                if (index < q.length - 1) {
                    dist = dist[item];
                } else {
                    dist[item] = value;
                }
            });
        b.setItem(o, JSON.stringify(p));
        return p;
    } else {

更改的部分:

  1. dist变量已创建
  2. 硬编码开关替换为foreach

答案 1 :(得分:0)

您可以尝试这样的操作,如果路径不存在,则值为null:

function retreiveValueFromObject(path, object) {

	var pathList = path.split(".");

	var currentValue = object;
	var brokeEarly = false;

	for (var i = 0; i < pathList.length; i++) {

		if (currentValue[pathList[i]]) {
			currentValue = currentValue[pathList[i]];
		} else {
			brokeEarly = true;
			break;
		}
	}

	return {
		value: currentValue,
		brokeEarly: brokeEarly
	};

}

function setValueInObject(path, value, object) {

	var nestedObj = retreiveValueFromObject(path, object).value;

	var pathList = path.split(".");
	var finalKey = pathList[pathList.length - 1];
	nestedObj[finalKey] = value;

}

var someObject = {
	a: {
		c: {
			d: "value"
		},
		z: "c"
	},
	b: {
		f: {
			x: "world"
		},
		g: "hello"
	},
};

console.log(retreiveValueFromObject("b.f.x", someObject));

setValueInObject("b.f.y", "newValue", someObject);

console.log(someObject);

答案 2 :(得分:0)

您要查找的是一点递归,我刚刚实现了update方法。

let localStorageHandler = function() {
    let b = window.localStorage,
         _t = this;
    _t.get = function(a) {
        try {
            return JSON.parse(b.getItem(a))
        } catch (c) {
            return b.getItem(a)
        }
    };

    function descendAndUpdate(obj, path, value) {
        let current = path[0],
            remainingPath = path.slice(1);
        // found and update
        if (obj.hasOwnProperty(current) && remainingPath.length === 0) {
            obj[current] = value;
        // found but still not there
        } else if (obj.hasOwnProperty(current)) {
            return descendAndUpdate(obj[current], remainingPath, value);
        }
        // if you want do add new properties use:
        // obj[current] = value;
        // in the else clause
        else {
            throw('can not update unknown property');
        }
    }

    _t.set = function(path, value) { // Update a single value or object
        if (~path.indexOf(".")) {
            let o = path.split(".")[0],
                p = this.get(o),
                q = path.split(".").slice(1);
            descendAndUpdate(p, q, value);
            console.log(p);
            b.setItem(o, JSON.stringify(p));
            return p;
        } else {
           b.setItem(path, JSON.stringify(value));
            return this.get(path);
        }
    };
    _t.remove = function(a) { // removes a single object from localstorage
        let c = !1;
        a = "number" === typeof a ? this.key(a) : a;
        a in b && (c = !0, b.removeItem(a));
        return c
    };
};
let local = new localStorageHandler();


// Create user and session info if it doesn't exist
let blankUser = new Object({
    alias: '',
    dob: '',
    PIN: '',
    level: 0,
    name: {
        first: '',
        last: '',
        mi:'',
    },
    session: {
        token: '',
        timeout: true,
        lastChange: Date.now()
    }
});

local.remove('user');

// Loads user data into localstorage
if (!local.get('user')) {
     local.set('user', blankUser);
}

local.set('user.session.timeout', false);
local.set('user.name', {first:'john', last:'doe', mi:'c'});
local.set('user.PIN', 8675309);

// new property
// local.set('user.sunshine', { 'like': 'always' });

console.log(local.get('user'));

我的一个朋友总是喜欢堆栈而不是递归,这是第二种选择。无论如何,我同意这里的许多评论。您已经知道您的域模型。除非您有很好的理由使用此方法,否则请花更多时间对数据库中的这些对象进行序列化和反序列化。我的印象是,您将能够以一种更自然的方式处理数据,因为更新数据库中字段的方面将被抽象化。

答案 3 :(得分:0)

我目前正在从事类似的项目。我正在做的是将数据存储在我称为WordMatrix(https://github.com/SamM/Rephrase/blob/master/WordMatrix.js)的东西中,也许您可​​以在解决方案中使用类似的东西。

我的项目是一个WIP,但下一步实际上是添加对localStorage的支持。该项目本身是一个数据库编辑器,可与键=>值存储区一起使用。您可以在此处查看其原型:(https://samm.github.io/Rephrase/editor.html

一旦实现了localStorage方面,我将更新这篇文章。

答案 4 :(得分:0)

您的主题让我想起了最近的another topic

为增强我提供的answer,我向您建议这些功能:

// Function to get a nested element:
function obj_get_path(obj, path) {
  return path.split('.').reduce((accu, val) => accu[val] || 'Not found', obj);
}

// Function to set a nested element:
function obj_set_path(obj, path, value) {
  var result = obj;
  var paths = path.split('.');
  var len = paths.length;
  for (var i = 0; i < len - 1; i++) {
    result = result[paths[i]] || {};
  }
  result[paths[len - 1]] = value;
  return obj;
}

// Example object
var obj = {
  name0: 'A name',
  level0: {
    name1: 'An other name',
    level1: {
      level2: {
        name3: 'Name to be changed',
        text3: 'Some other text'
      }
    }
  }
}

// Use of the function
obj = obj_set_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_set_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_get_path(obj, 'level0.level1.level2');

// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct

⋅ ⋅ ⋅

我们也可以在上面的代码段中改编第二个函数,以便它同时用于get和set,具体取决于是否设置了“ value”:

// We could also adapt the second function for both uses:
function obj_path(obj, path, value = null) {
  var result = obj;
  var paths = path.split('.');
  var len = paths.length;
  for (var i = 0; i < len - 1; i++) {
    result = result[paths[i]] || {};
  }
  // Return result if there is no set value
  if (value === null) return result[paths[len - 1]];
  // Set value and return 
  result[paths[len - 1]] = value;
  return obj;

}

// Example object
var obj = {
  name0: 'A name',
  level0: {
    name1: 'An other name',
    level1: {
      level2: {
        name3: 'Name to be changed',
        text3: 'Some other text'
      }
    }
  }
}

// Use of the function
obj = obj_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_path(obj, 'level0.level1.level2');

// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct

希望有帮助。

答案 5 :(得分:0)

怎么样:

SELECT TOP 10 * 
FROM Rates 
WHERE cast(Date as date)= '2017-06-09'