通过选择器字符串

时间:2018-08-21 18:45:05

标签: javascript

我试图通过将键传递给方法来从对象中删除项目。例如,我想删除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不是在此处使用的正确方法。如何从对象中删除项目?

5 个答案:

答案 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循环要慢,但是如果数据集足够小或您的用例可接受的可读性与效率之间的折衷,则可能是一个很好的选择。

https://hackernoon.com/javascript-performance-test-for-vs-for-each-vs-map-reduce-filter-find-32c1113f19d7

答案 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来从对象中删除嵌套键的特定用例使用命名空间字符串作为输入,我认为任何数量的循环或解析都不会更高效或更简洁。