【问题标题】:Convert dot notation string to object and lookup reference object将点符号字符串转换为对象并查找引用对象
【发布时间】:2020-09-02 07:25:22
【问题描述】:

我有一个点表示的数组,例如:

var base = ['a.b.c','a.e','h'];

我还有一个参考对象:

var reference = { 
  a: { 
    b: { 
      c:"Hello", 
      d:"you" 
    }, 
    e:"beautiful",
    f:"and"
  },
  g:"kind",
  h:"World" 
};

我需要从基本标记字符串构建一个对象并从引用对象中获取值。不幸的是,我无法更改基准或参考。

我让它适用于数组的每个单独部分,但问题是当我尝试合并对象时。它用 a.e 覆盖值 a.b.c

我想要的输出是:

var desiredOutput = { 
  a: { 
    b: { 
      c:"Hello" 
    }, 
    e:"beautiful" 
  }, 
  h:"World" 
};

但是,我的输出下面的代码是:

var output = { 
    a: { 
        e: "beautiful" 
    },
    h: "World" 
};

任何帮助将不胜感激。我仅限于 Object.assign。

function convert(base,reference) {
    var obj = {};

    var getObjectValue = function(string, reference) {
        var i = 0;
        while (reference && i < string.length) {
            reference = reference[string[i++]];
        }
        return reference;
    };

    for (var n = 0; n < base.length; n++) {    
        var s = base[n].split("."),
            x = obj;

        for(var i = 0; i < s.length-1; i++) {
            x = x[s[i]] = {}; 
        }

        x[s[i]] = getObjectValue(s,reference);
    }
    return obj;
}

var base = ['a.b.c','a.e','h'];

var reference = { 
  a: { 
    b: { 
      c:"Hello", 
      d:"you" 
    }, 
    e:"beautiful",
    f:"and"
  },
  g:"kind",
  h:"World" 
};

var desiredOutput = { 
  a: { 
    b: { 
      c:"Hello" 
    }, 
    e:"beautiful" 
  }, 
  h:"World" 
};

console.log(convert(base,reference));

【问题讨论】:

  • 您是否可以导入一些第三方模块,例如 lodash?
  • 不幸的是没有第 3 方库

标签: javascript object append assign


【解决方案1】:

与你的

x = x[s[i]] = {}; 

即使 s[i] 属性已被填充,您也会无条件地覆盖它。仅当该属性中不存在另一个对象时才分配一个新对象。

if (x[s[i]]) {
  x = x[s[i]];
} else {
  x = x[s[i]] = {};
}

function convert(base,reference) {
    var obj = {};

    var getObjectValue = function(string, reference) {
        var i = 0;
        while (reference && i < string.length) {
            reference = reference[string[i++]];
        }
        return reference;
    };

    for (var n = 0; n < base.length; n++) {    
        var s = base[n].split("."),
            x = obj;

        for(var i = 0; i < s.length-1; i++) {
            if (x[s[i]]) {
              x = x[s[i]];
            } else {
              x = x[s[i]] = {};
            }
        }

        x[s[i]] = getObjectValue(s,reference);
    }

    return obj;

}

var base = ['a.b.c','a.e','h'];

var reference = { 
  a: { 
    b: { 
      c:"Hello", 
      d:"you" 
    }, 
    e:"beautiful",
    f:"and"
  },
  g:"kind",
  h:"World" 
};

var desiredOutput = { 
  a: { 
    b: { 
      c:"Hello" 
    }, 
    e:"beautiful" 
  }, 
  h:"World" 
};


console.log(convert(base,reference));

这就是我的处理方式:

var base = ['a.b.c','a.e','h'];
var reference = { 
  a: { 
    b: { 
      c:"Hello", 
      d:"you" 
    }, 
    e:"beautiful",
    f:"and"
  },
  g:"kind",
  h:"World" 
};

const results = {};
// Helper function to get the nested value from an array of properties:
const getVal = props => props.reduce((a, b) => a[b], reference);
for (const propStr of base) {
  // Turn string into an array of properties:
  const props = propStr.split('.');
  // Get nested value to assign at the end:
  const val = getVal(props);
  // Get last property to assign on the last object:
  const lastProp = props.pop();
  let obj = results;
  // Iterate through to the nested object on the `results`,
  // creating objects on the way only if they don't exist yet:
  while (props.length) {
    const prop = props.shift();
    if (obj[prop]) obj = obj[prop];
    else {
      obj[prop] = {};
      obj = obj[prop];
    }
  }
  obj[lastProp] = val;
}
console.log(results);

【讨论】:

  • 经典案例,看起来太长了...感谢您指出我的问题并提供了一个不错的 ES6+ 解决方案。不幸的是,我在处理
  • 你的 ES6 代码没有返回正确的结果: > Object { b: Object { a: Object { c: "Hello" } }, a: Object { e: "beautiful" }, h: "World" } 它将 b 添加到顶部而不是 a 之下。由于您的修复,仍然接受是答案。但是,如果您可以修复您的 ES6 代码或不折叠您已修复的原始代码,那将不胜感激;)
  • 糟糕,需要使用shift 来获取数组中的顶部项,而不是.pop。鉴于现在是 2020 年,你真的应该至少在 ES2015 中编写源代码。如果您需要支持不理解 ES2015 语法的古老过时浏览器,行之有效的专业解决方案是使用Babel 将代码自动转译为 ES5 用于生产,同时保留 source i> 以干净、简洁、易读的 ES2015+ 语法编写代码。
  • 不能同意更多 - 问题是我正在使用的系统使用 Javascript 1.5 和一个自我实现的 JINT 解释器......这是一个被数百万人使用的众所周知的系统,但很快就会更新机器人.使用 babel 将是一种选择 - 谢谢
【解决方案2】:

这可以在没有while循环的情况下实现!

我只使用了 es6 的 Array.prototype.reduce(和语义,但您可以自行转换/转换它们)。您也可以轻松地对其进行 poly/ponyfill,参见 this article

const base = ['a.b.c', 'a.e', 'h'];

const reference = { 
  a: { 
    b: { 
      c: 'Hello', 
      d: 'you' 
    }, 
    e: 'beautiful',
    f: 'and'
  },
  g: 'kind',
  h: 'World' 
};

const isObject = item => (item && typeof item === 'object' && !Array.isArray(item) && item !== null);

const pick = (obj, keys) => keys.split('.').reduce((prev, key) => {
  const ret = (prev || obj);
  if (isObject(ret) && key in ret) {
    return ret[key];
  }

  return prev;
}, null);

const extend = (target, source) => {
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach((key) => {
      if (isObject(source[key])) {
        if (!target[key] || !isObject(target[key])) {
          target[key] = source[key];
        }
        extend(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    });
  }
  return target || source;
};

const fromDotNotation = (dotNotation, value) => {
  const ret = {};
  dotNotation.split('.').reduce((prev, key, i, self) => {
    return prev[key] = (i === self.length - 1) ? value : prev[key] || {};
  }, ret);

  return ret;
}

const fromDotNotations = (source, dotNotations) => dotNotations.reduce((prev, dotNotation) => {
  return extend(prev, fromDotNotation(dotNotation, pick(source, dotNotation)));
}, {});

const output = fromDotNotations(reference, base);

console.log(JSON.stringify(output, null, 2));

在这里,我们希望能够从任何点符号构建一个对象,其尾部有一个初始值。我们将使用采摘方法从引用对象中选择初始值。循环多个点符号,我们可以创建一个构造对象数组。只需使用合并方法,我们就可以创建一个目标对象,我们希望将我们构建的对象合并到该目标对象中。

【讨论】:

    【解决方案3】:

    我创建了以下代码 sn-p 以使用嵌套 foreach 以简单的方式获得您想要的结果,希望这对您有所帮助。我在cmets中描述了代码。

    var reference = {
      a: {
        b: {
          c: "Hello",
          d: "you"
        },
        e: "beautiful",
        f: "and"
      },
      g: "kind",
      h: "World"
    };
    
    var base = ['a.b.c', 'a.e', 'h'];
    
    var result = {};
    
    base.forEach(str => {
    
      //split the base by `.`
      var arr = str.split('.');
    
      //get the value
      var val = arr.reduce((r, el) => r[el], reference);
    
      //set the initial temporary path of resulting object
      var tmp = result;
    
      arr.forEach((el, i) => {
    
        //set final property value into resulting object
        if (i == arr.length - 1)
          return tmp[el] = val;
    
        //create nested property in resulting object
        if (!tmp[el])
          tmp[el] = {};
    
        //update temporary path
        tmp = tmp[el];
      });
    });
    
    console.log(result)

    【讨论】:

      猜你喜欢
      • 2011-10-05
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2011-12-09
      • 2019-12-17
      • 1970-01-01
      • 2014-01-14
      相关资源
      最近更新 更多