我试图通过将键传递给方法来从对象中删除项目。例如,我想删除a1
,并为此将a.a1
传递给该方法。然后,它应该从对象中删除a1
,而剩下对象的其余部分。
这是对象的结构:
this.record = {
id: '',
expiration: 0,
data: {
a: {
a1: 'Cat'
}
}
}
然后我调用此方法:
delete(key) {
let path = key.split('.')
let data = path.reduce((obj, key) => typeof obj == 'object' ? obj[key] : null, this.record.data)
if(data) delete data
}
赞:
let inst = new MyClass()
inst.delete('a.a1')
这给了我以下错误:
delete data; ^^^^
SyntaxError:在严格模式下删除不合格的标识符。
我认为data
仍是参考,还是不是?
也许reduce
不是在此处使用的正确方法。如何从对象中删除项目?
答案 0 :(得分:3)
在您的示例中,data
的值在检查其真实性时为Cat
,即您要删除的属性的 value 。此时,data
只是一个引用字符串的常规变量,不再在inst
的上下文中。
以下是我设法使用您的OP中的一个作为解决方案的解决方案:
let path = key.split('.')
let owningObject = path.slice(0, path.length - 1)
.reduce((obj, key) => typeof obj == 'object' ? obj[key] : null, this.record.data)
if (owningObject) delete owningObject[path[path.length - 1]]
此操作与您拥有的操作的主要区别在于reduce
在路径段的 slice 上操作,该路径段不包含最终标识符:这以{{1 }}是对owningObject
对象的引用。 a
实际上只是沿着路径导航,直到倒数第二个片段,该片段本身用作要删除的属性名称。
对于无效路径,由于reduce
或因为在未知属性上使用if (owningObject)
都是禁止操作,因此无法通过。
答案 1 :(得分:0)
我想出的解决方案虽然不怎么喜欢,但是可以起作用,它遍历所有项,这样我就可以做长键了
a.a1
a.a1.a1-1
a.a1.a1-1.sub
该函数如下所示
let record = {
data: {
a: {
a1: 'Cat',
a2: {
val: 'Dog'
}
}
}
}
function remove(key) {
let path = key.split('.')
let obj = record.data
for (let i = 0; i < path.length; i++) {
if (i + 1 == path.length && obj && obj[path[i]]) delete obj[path[i]]
else if(obj && obj[path[i]]) obj = obj[path[i]]
else obj = null
}
}
// Removes `a.a1`
remove('a.a1')
console.log(JSON.stringify(record))
// Removes `a.a2.val`
remove('a.a2.val')
console.log(JSON.stringify(record))
// Removes nothing since the path is invalid
remove('a.a2.val.asdf.fsdf')
console.log(JSON.stringify(record))
答案 2 :(得分:0)
您可以使用[]参考删除键。
var foo = {
a: 1,
b: 2
};
var selector = "a";
delete foo[selector];
console.log(foo);
我不确定这是否对您有帮助,但可能会帮助某些人研究这个问题。
答案 3 :(得分:0)
这是另一种方法,与OP自己的解决方案非常相似,但是使用Array.prototype.forEach
来遍历路径部分。我尝试独立地获得此结果,以尝试尽可能优雅地将其包装。
function TestRecord(id, data) {
let record = {
id : id,
data : data
};
function removeDataProperty(key) {
let parent = record.data;
let parts = key.split('.');
let l = parts.length - 1;
parts.forEach((p, i) => {
if (i < l && parent[p]) parent = parent[p];
else if (i == l && parent[p]) delete parent[p];
else throw new Error('invalid key');
});
}
return {
record : record,
remove : function(key) {
try {
removeDataProperty(key);
} catch (e) {
console.warn(`key ${key} not found`);
}
}
}
}
let test = new TestRecord('TESTA', {
a : { a1 : '1', a2 : '2' },
b : { c : { d : '3' } }
});
test.remove('a'); // root level properties are supported
test.remove('b.c.d'); // deep nested properties are supported
test.remove('a.b.x'); // early exit loop and warn that property not found
console.log(test.record.data);
在此示例中使用throw
是为了在路径的任何部分无效的情况下尽早退出循环,因为forEach
不支持break
语句。
顺便说一下,有证据表明forEach
比简单的for
循环要慢,但是如果数据集足够小或您的用例可接受的可读性与效率之间的折衷,则可能是一个很好的选择。
答案 4 :(得分:-1)
这可能不是最优雅的解决方案,但是您可以使用eval()
轻松快速地获得所需的结果。
function TestRecord(id) {
let record = {
id : id,
data : {
a : {
a1 : 'z',
a2 : 'y'
}
}
};
return {
record : record,
remove : function (key) {
if (!key.match(/^(?!.*\.$)(?:[a-z][a-z\d]*\.?)+$/i)) {
console.warn('invalid path');
return;
} else {
let cmd = 'delete this.record.data.' + key;
eval(cmd);
}
}
};
}
let t = new TestRecord('TESTA');
t.remove('a.a1');
console.log(t.record.data);
我提供了一个regular expression from another answer,用于根据名称空间格式验证用户输入,以防止滥用/滥用。
顺便说一句,我也使用了方法名remove
而不是delete
,因为delete是reserved keyword in javascript。
此外,在反评估下降投票开始涌入之前。发自:https://humanwhocodes.com/blog/2013/06/25/eval-isnt-evil-just-misunderstood/:
...当您遇到eval()的情况时,不要害怕使用它 说得通。尝试不要先使用它,但不要让任何人吓到你 当eval()为时认为代码更脆弱或更不安全 正确使用。
我并没有将eval推广为操作对象的最佳方法(显然,具有良好接口的良好定义的对象将是正确的解决方案),但针对通过传递a来从对象中删除嵌套键的特定用例使用命名空间字符串作为输入,我认为任何数量的循环或解析都不会更高效或更简洁。