【问题标题】:object key deleted but still iterated in loop对象键已删除但仍在循环中迭代
【发布时间】:2020-03-25 18:54:03
【问题描述】:

鉴于 EcmaScript 第 12.6.4 节:

被枚举对象的属性可能会在期间被删除 枚举。如果在此期间尚未访问的属性 枚举被删除,则不会被访问。

但是,看看这个案例:

class Lead {
    MAPPINGS = [
        [
            /Medication_Types_(\d+)/,
            (data, key, acc, i) => {
                const values = Object.keys(data)
                                     .filter( key => /Medication_Types_(\d+)/.test(key ))
                                     .map(key => data[key]);

                console.log('the values: ', values);
                acc['Medication_Types'] = values;

                Object.keys(data).forEach( key => {
                    if( /Medication_Types_(\d+)/.test(key)) delete data[key];
                })

                return i;
            }
        ]
    ]

    format(data) {
        const copy = JSON.parse(JSON.stringify(data));
        let mapping = false;
        let i = 1;

        const result = Object.keys(copy).reduce((acc, key) => {
            mapping = false;
            this.MAPPINGS.forEach( (mapping) => {
                if(mapping[0].test(key)){
                    mapping[1](copy, key, acc, i);
                    mapping = true;
                }
            });

            if(!mapping){
                acc[key] = copy[key];
            }

            return acc;
        }, {});

        return result;
    }
}

const data = {
    "Gender" : "Male",
    "Medication_Types_1" : "A",
    "Medication_Types_2" : "B",
    "First_Name" : "bob"
}

const lead = new Lead();
const result = lead.format(data);
console.log('the result: ', result);

它输出:

the values:  [ 'A', 'B' ]
the values:  []
the result:  {
  Gender: 'Male',
  Medication_Types: [],
  Medication_Types_1: undefined,
  Medication_Types_2: undefined,
  First_Name: 'bob'
}

如果键被真正删除,那么“值”就不会第二次打印出来,而是我会得到(以及我想要的)这个:

{
      Gender: 'Male',
      Medication_Types: ['A', 'B'],
      First_Name: 'bob'
}

所以看起来delete 可能正在删除密钥,但不是从当前迭代中删除,但 EcmaScript 声明它将从当前迭代中删除!出了什么问题,我该如何解决?

【问题讨论】:

  • 您引用的是哪个 EcmaScript 版本?在current revision 中,§12.6.2 与枚举无关。
  • @Bergi 我偶然引用了 5 而不是 6。但我假设如果它在 5 中,它会在 6 中。
  • 当前版本是 10,枚举是通过那里的迭代器描述的 :-)

标签: javascript node.js ecmascript-6


【解决方案1】:

您没有进行枚举。您正在创建一个数组,然后在该数组的 forEach 迭代期间删除属性:

const keys = Object.keys(data);
console.log(keys); // at this point, all the keys are already in the array
keys.forEach( key => {
    if( /Medication_Types_(\d+)/.test(key)) delete data[key];
})

当您从对象中delete 属性时,它不会从keys 数组中删除。在您的reduce 通话中也会发生同样的情况。您可以通过使用真正的 for … in 循环来避免这种情况。


但是,您的代码中的实际错误是

mapping = false;
this.MAPPINGS.forEach((mapping) => {
//                      ^^^^^^^
    if (mapping[0].test(key)){
        mapping[1](copy, key, acc, i);
        mapping = true;
//      ^^^^^^^
    }
});

您没有将外部范围布尔值mapping 变量设置为true,这将防止值被复制到结果中,而是您只需覆盖forEach 回调的mapping 参数。

【讨论】:

  • 可能最好的解决方案是在枚举之前创建一个 groupBy 函数(和组相关键)?
  • @Donato 可能。我对您的问题进行了更深入的研究,发现意外结果是由于另一个错误引起的,与未删除该属性无关。
  • 在将代码复制到 SO 中时,重复使用映射 false 是无意的结果。即使映射是不同的值,原始问题仍然存在。真正的问题是我正在改变一个对象,但在数组上进行迭代。注意到这一点的好眼睛。我的快速解决方案是使用数组并使用拼接函数来删除项目。
猜你喜欢
  • 1970-01-01
  • 2013-08-13
  • 2012-08-19
  • 1970-01-01
  • 2011-07-17
  • 1970-01-01
  • 2017-06-23
  • 1970-01-01
  • 2014-06-04
相关资源
最近更新 更多