【问题标题】:Better way to update an object's value at a variable depth在可变深度更新对象值的更好方法
【发布时间】:2018-07-03 01:17:33
【问题描述】:

我正在开发一些使用处理程序在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) 不将父对象与 @ 987654328@ 值更新后?


这个问题与我使用localStorage无关。我最初在代码审查中发布了这个问题,这需要一个工作上下文示例。他们立即关闭了这个问题,因为我的部分问题是我提供的代码在您开始处理更新嵌套超过六个对象的值时不起作用。虽然 我可以无限期地继续我的 switch 语句,但这正是我想要避免的

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

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

所有这些值,虽然设置在不同的时间,只是更新或创建一个值,它们不会清除其他地方的任何预先存在的值。

【问题讨论】:

  • lodash 有一个叫做 set 的函数,这可能是你想要的 -> lodash.com/docs/4.17.10#set
  • 我应该提到的是项目的所有者想要保持原样。我们的最后一位开发人员烧毁了一些桥梁,将大量插件塞入我们现在要替换的网站。但我会看一下 lodash 的源代码 :)
  • 您可以更改对象,然后将对象保存在localStorage中
  • peek at the source code for lodash,.. 你当然可以这样做.. 我可以理解代码膨胀的问题,我认为 lodash 的开发人员理解这一点,这就是你可以挑选位的原因你只需要。假设你使用的是构建系统(ps。你应该是),你可以做require("lodash/set")
  • 您目前似乎在问的是从点符号字符串中遍历您的对象。然后它是stackoverflow.com/questions/6393943/… 的副本,但这听起来像一个糟糕的设计模式,我不明白为什么你不能将你的对象直接存储为 JSON。 (在页面加载时反序列化一次,仅在真正需要时序列化(例如在卸载时可能就足够了)

标签: javascript object local-storage


【解决方案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

【讨论】:

    【解决方案2】:

    你可以试试这样,如果路径不存在,则值为空:

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

    【讨论】:

    【解决方案3】:

    你要找的是一点点递归,我只是实现了更新方法。

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

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

    【讨论】:

      【解决方案4】:

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

      我的项目是一个 WIP,但下一步实际上是添加对 localStorage 的支持。项目本身是一个数据库编辑器,可以使用 key => value 存储。你可以在这里查看它的原型:(https://samm.github.io/Rephrase/editor.html)

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

      【讨论】:

        【解决方案5】:

        你的话题让我想起了最近的一个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

        ⋅ ⋅ ⋅

        我们还可以调整上面 sn-p 中的第二个函数,使其适用于 get 和 set,具体取决于是否设置了“值”:

        // 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

        希望对你有帮助。

        【讨论】:

          【解决方案6】:

          怎么样:

          function parse(str) {
              var arr = str.split('.');
              return function(obj) {
                 return arr.reduce((o, i) => o[i], obj);
              }
          }
          
          
          let foo = {
             a: {
                 b: {
                    c: {
                       bar: 0
                    }
                 }
             }
          
          }
          
          let c = parse('a.b.c')(foo);
          console.log(c.bar);
          c['bar'] = 1;
          console.log(foo);

          【讨论】:

            猜你喜欢
            • 2018-03-14
            • 2018-06-04
            • 2018-11-01
            • 2019-11-05
            • 1970-01-01
            • 2014-01-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多