【问题标题】:OR (||) operator in angular.forEach not working?angular.forEach 中的 OR (||) 运算符不起作用?
【发布时间】:2016-06-08 14:18:11
【问题描述】:

我有如下的json数组,需要通过一定的条件去掉一些:

var data = [
             {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
             {data1: "ddd", data2: "eee", data3: "fff"},  // Second
             {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
             {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
           ];
angular.forEach(data, (item, i) => {
  if (item.data2 == 'eee' || item.data3 == 'iii') {
    data.splice(i, 1);
  }
});
console.log(data);

在上述情况下,我需要从数据数组中删除第二个(data2 有“eee”)、第三个(data3 有“iii”)和第四个(data2 是“eee”)对象。 但是第三个对象并没有被拼接,而是保留在数据数组中。

我可以以这种方式使用 OR (||) 运算符吗?如果不是,使用多个条件从数组中删除元素的正确方法是什么?

我已经为这个问题摸索了好几个小时,但也许我错过了一些东西。

【问题讨论】:

  • 问题不在于|| 运算符:不要在循环内使用splice
  • 不要改变你正在迭代的数组,最好使用filter
  • 迭代时移除项目的问题是大小变化和索引会有你不想要的行为。解决方法是反向循环:jsfiddle.net/joherro3/pca5Lbgg/1 但最好使用过滤器。
  • 我删除了我原来的答案并发布了一个更完整的解决方案。

标签: javascript arrays angularjs


【解决方案1】:

使用filter 而不是splice

splice 与项目的索引相关,因此当您在循环中删除项目时,索引号将变为无效。

var data = [
             {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
             {data1: "ddd", data2: "eee", data3: "fff"},  // Second
             {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
             {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
           ];

function keepItemCondition(item) {
  return !(item.data2 == 'eee' || item.data3 == 'iii');
}

var filtered = data.filter(keepItemCondition);

console.log(filtered);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

【讨论】:

  • 感谢@j3ff 的精彩回答!
【解决方案2】:

改用过滤器, 使用 splice 时,您可以更改要迭代的数组,该数组是否:

var data = [
             {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
             {data1: "ddd", data2: "eee", data3: "fff"},  // Second
             {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
             {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
           ];
var filtered = data.filter(function (element) {
    return element.data2 !== 'eee' && element.data3 !== 'iii';
});
console.log(filtered);

【讨论】:

  • 对正确答案投赞成票,但您应该解释为什么 splice 不起作用。
  • 多么快速,漂亮。没想到我的问题这么快就解决了!谢谢大家的快速见解!并感谢@korven 和 j3ff 的出色回答!
  • @Köttur 所以你想要一个快速的解决方案,而不是一个专门解决你的问题并解释你的代码有什么问题的答案......?
  • @8protons 其实我已经阅读了你的详细解释......是的,当我检查控制台输出时,我首先认为 OR 运算符正在工作......现在我明白为什么 splice (i, n) 不起作用。我只是认为它会简单地删除索引“i”中的“n”元素。我应该更仔细地阅读文档。
  • @JoseHermosillaRodrigo 谢谢你的建议!!你的解决方案很有趣。回到办公室后,我想试试你的方法。谢谢!!
【解决方案3】:

在控制台检查输出时,你会看到返回的是一个数组:

[Object, Object]

其中包含

[{'aaa', 'bbb', 'ccc'}, {'ggg', 'hhh', 'iii'}]

这告诉我们您的代码正在删除第二个和第四个数组对象。作为证据,注释掉您的foreach 例程以发现原始数组的完整性得以保留。

为什么?好吧,您的逻辑 OR ( || ) 运算符正在按预期工作!

angular.forEach(data, (item, i) => {
  if (item.data2 == 'eee' || item.data3 == 'iii') {
    data.splice(i, 1);
  }
});

当正在检查的当前对象中的对象项data2 == 'eee'data3 == 'iii' 时,您的代码将拼接(换句话说,删除)从i 开始并结束于的元素idata 数组中。这通过您写splice(i, 1) 的事实来表达;例如,您是否写过类似splice(i, 3) 的东西,您会删除从i 开始并以i+2 结束的对象元素。另一种看待方式是说,“splice(i, n) 表示删除从索引 i 开始的 n 元素”。

因此,每次评估比较表达式时,您都在命令当前元素 ionly 1 element from i(即i 本身)从 data 数组中删除;当前元素恰好是包含eeeiiiobjects。如果必须,请遍历您的循环,计数 i 以向自己证明 data[1]data[3] 正在被删除。

如果您在 item data2 == eee 或 item data3 == iii 时询问如何删除对象的特定项目,请考虑以下内容:

var data = [
             {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
             {data1: "ddd", data2: "eee", data3: "fff"},  // Second
             {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
             {data1: "jjj", data2: "eee", data3: "lll"}   // Fourt
           ];
angular.forEach(data, (item, i) => {
  if (item.data2 == 'eee') {
    delete data[i].data2;   
  } 
  else if (item.data3 == 'iii') {
    delete data[i].data3;
  }
});
console.log(data);

返回:

[{'aaa', 'bbb', 'ccc'}, {'ddd', 'fff'}, {'ggg', 'hhh'}, {'jjj, 'lll'}]

JSFiddle 上有一个工作演示。尽管您的 OR ( || ) 表达式确实“有效”,但它并不完全符合您的预期,因为 splice 被用于删除 一个对象 而不是一个项目。因此,我们必须稍微改变一下我们的逻辑。

【讨论】:

    【解决方案4】:

    正如我评论的那样,您的代码不起作用,因为在遍历数组时,如果您删除一个项目,下一个项目将放置在已删除项目的索引中,索引增加一个,并且永远不会评估该项目。

    var data = [
      {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
      {data1: "ddd", data2: "eee", data3: "fff"},  // Second
      {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
      {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
    ];
    
    // Won't work
    for(var i = 0; i < data.length; i++){
      var item = data [i];
      if (item.data2 == 'eee' || item.data3 == 'iii') {
        data.splice(i, 1);
      }
    }
    console.log(data);

    一种解决方案是以反向模式循环。在这种情况下,删除一项不会影响下一项。

    var data = [
      {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
      {data1: "ddd", data2: "eee", data3: "fff"},  // Second
      {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
      {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
    ];
    
    for(var i = data.length -1; i >= 0; i--)
      if (data[i].data2 == 'eee' || data[i].data3 == 'iii')
        data.splice(i, 1);
    
    console.log(data);

    如果你想保持 forEach 循环风格,你可以自己做forEachReverse

    Array.prototype.forEachReverse = function (callback){ 
      var arr = this;
      for(var i = arr.length -1; i >= 0; i--) 
      	callback(arr[i], i, arr);
    };
    
    
    var data = [
      {data1: "aaa", data2: "bbb", data3: "ccc"},  // First
      {data1: "ddd", data2: "eee", data3: "fff"},  // Second
      {data1: "ggg", data2: "hhh", data3: "iii"},  // Third
      {data1: "jjj", data2: "eee", data3: "lll"}   // Fourth
    ];
    
    data.forEachReverse((item, i)=>{
      if (item.data2 == 'eee' || item.data3 == 'iii') {
        data.splice(i, 1);
      }
    });
    
    console.log(data);

    正如其他答案所建议的那样,filter 更干净,并且会根据条件过滤您的列表。

    【讨论】:

    • 谢谢您的详细解释!!我在这里发布的问题实际上是代码的简化版本。所以今天,我在“真实”代码上尝试了所有人教给我的所有解决方案。正如您所指出的,在我的真实代码中,循环中的项目从未评估过,因为我实际上一直在删除它自己的数组。我也追踪了你代码的每一部分。有了您的解决方案的想法,现在一切都在我想要的地方。谢谢!!
    • 不客气!! =)
    猜你喜欢
    • 2015-02-26
    • 1970-01-01
    • 2018-12-28
    • 1970-01-01
    • 2017-12-04
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多