Javascript 通过分组camelCase属性创建对象

Javascript 通过分组camelCase属性创建对象,javascript,arrays,object,Javascript,Arrays,Object,最近,我发现我必须从HTML标记上的属性创建一个对象。我在AngularJS环境中执行此操作,因此连字符属性转换为camelCase,但我也可以使用data-attributes和dataset 例如,我有: <element person-name="Grant" animation-jump="123" /> 我的问题是,然后我想将camelCase对象转换为结构化对象: { "person" : { "name" : "Grant" }, "animation

最近,我发现我必须从HTML标记上的属性创建一个对象。我在AngularJS环境中执行此操作,因此连字符属性转换为camelCase,但我也可以使用
data-
attributes和
dataset

例如,我有:

<element person-name="Grant" animation-jump="123" />
我的问题是,然后我想将camelCase对象转换为结构化对象:

{
  "person" : {
    "name" : "Grant" },
  "animation" : {
    "jump" : "123" }
}
我已经创建了我的QUint单元测试的JSFIDLE
它实际上适用于我想要的仅为1个级别的情况,但我希望它适用于任何级别,因为我预见到它将被需要,因此我可以公开发布代码。

这不是最佳解决方案,但在这种特殊情况下,您可以通过将数组转换为字符串来实际使用数组作为键:

obj[["animation","Jump"].join()] = "123";

这将适用于您的原始对象。

您不能以这种方式使用它,我不认为这是一个合乎逻辑的建议。下面我解释为什么它不会

obj[["animation","jump"]] = "123"
换成

obj["animation"]["jump"] = "123"
一切都很好

为什么我不支持你的想法

  • 使用起来很凌乱,没有风格可言
  • 将数组用作对象键是没有逻辑的
  • 还有另一种按键调用对象项的方法:使用点,这不支持您的想法。我想每个人都能想象为什么

我们将使用
reduce
循环对象的键,建立结果。我们将每个键分解为其组件,例如
personName
分解为
person
name
。我们在这些组件上循环,如果子对象不存在,则创建子对象。最后,我们将最后一个组件添加到最里面的子对象中,作为具有相关值的属性

Object.keys(input).reduce((result, key) => {
  var parts = key.match( /(^|[A-Z])[a-z]+/g) . map(part => part.toLowerCase());
  var leaf = parts.pop();
  var obj = result;

  parts.forEach(part => obj = obj[part] = obj[part] || {});
  obj[leaf] = input[key];

  return result;
}, {});
使用字符串的解决方案

var obj={“animationsJump”:“123”,“animationsRun”:“456”,“animationsHide”:“789”,“personName”:“Grant”,“personPetsDog”:“Snowy”,“personPetsCat”:“Snowball”},
newObject={};
Object.keys(obj.forEach)(函数(k){
var path=k.split(/(?=[A-Z])/).map(函数){
返回s.toLowerCase();
}),
last=path.pop();
路径还原(函数(r,a){
r[a]=r[a]|{};
返回r[a];
},newObject)[last]=obj[k];
});

document.write(''+JSON.stringify(newObject,0,4)+'')为什么首先需要将属性转换为camelCase。。?照办

var o1; // <- undefined
o1["myProp"] = 1; // Uncaught TypeError: Cannot set property 'myProp' of undefined
此外,我想提醒一下,只有在使用括号表示法的引用预定义为对象时,才可能使用括号表示法创建属性。比如

var o2 = {}; // Object {}
o2["myProp"] = 1; // <- 1

虽然我喜欢通过前瞻(
/?=[a-Z]/
)拆分camelCase道具的方法,但它需要额外的工作来降低整个道具字符串数组的大小写,不管它们是否已经小写。所以我想这可能会稍微快一点。(…或者不是因为它的递归性质)

删除了ECMA位-你认为你可以编辑我的JSFIDLE吗?你应该使用
deepEqual
进行测试。如果camelCase属性中有两个或更多连续的大写字母,例如
“fancyGirlTakesARide”
,这个解决方案就会中断。它只考虑最后的后续资本。因此,不会为上述属性创建
对象。当然,对于
fancyGirlWritesCSS
,它会失败。在这种情况下,你必须决定你想要的行为是什么。如果希望单个大写字母成为单个属性,则将
+
后面的
[a-z]
替换为
*
。如果您希望分段允许多个相邻的大写字母,那么在
[a-Z]
之后添加一个
+
。我猜您决定将连续的大写字母分开,或者将它们视为一个,但绝对不会将它们全部丢弃,直到后面跟一个小写字母(如果存在)为止这将是信息的丢失。因此,正确的正则表达式不是答案中的正则表达式,而是
/(^ |[A-Z])[A-Z]*/g
/(^ |[A-Z]+)[A-Z]*/g
在split regexp中很好地使用了lookahead。我在现实世界中使用AngularJS,它将连字符属性转换为camelCase@LukeT'Brien好吧,如果你一定要处理camelCase道具,那么上面我已经修改了我的解决方案,使之成为一个完整的解决方案。
var o1; // <- undefined
o1["myProp"] = 1; // Uncaught TypeError: Cannot set property 'myProp' of undefined
var o2 = {}; // Object {}
o2["myProp"] = 1; // <- 1
o2["myProp"]["myOtherProp"] = 2; // <- 2 but won't type coerce o2.myProp to Object
var inp = {"personName" : "Grant", "animationJump" : "123", "fancyGirlTakesARide" : "987"},
 result = Object.keys(inp).reduce(function(p,c,i){
                                    var props = c.replace(/[A-Z]/g, m => "-" + m.toLowerCase()).split("-");
                                    return arrayToStructuredObject(p,props,inp[c])                    
                                  },{});

function arrayToStructuredObject(obj,props,val){
  if (props.length){
    obj[props[0]] = props.length > 1 ? {} : val;
    arrayToStructuredObject(obj[props.shift()],props,val);
  }
  return obj;
}