【问题标题】:Is it possible to delete dictionary from mutablearray while iterating? [duplicate]迭代时是否可以从 mutablearray 中删除字典? [复制]
【发布时间】:2014-10-03 23:53:51
【问题描述】:
for (int i = 0; i< [optionDataArr count]; i++) {
    NSString *sName  = [[optionDataArr objectAtIndex:i] objectForKey:kOptionName];
    NSString *sPrice = [[optionDataArr objectAtIndex:i] objectForKey:kOptionExtraPrice];

    if (sName.length == 0  && sPrice.length == 0) {
          [optionDataArr removeObjectAtIndex:i];
    }  
}

假设optionDataArr 包含一个没有值的字典,当执行上述代码时,我收到:

Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

【问题讨论】:

    标签: ios objective-c nsmutablearray nsdictionary


    【解决方案1】:

    您可以在使用普通的旧 for 循环时删除项目,而在使用快速枚举时则不能。

    不过,您的代码有问题。当您删除第 n 个元素时,下一个元素将是第 (n+2) 个。您需要手动将索引减一以考虑移位的元素。

    还请记住,在这种情况下,您确实需要对循环中的数组长度进行“实时”边界检查,而不仅仅是使用保存长度的临时变量(或者您需要将其递减为好)。

    【讨论】:

    • 没错。只需将您的代码替换为:pastie.org/9461426
    • thnku@Eiko @Szu 为您的关心
    • @Szu 它仍然给出相同的错误索引 0 超出空数组的界限'
    • @SKT 你是展示了你的实际代码,还是“剥离”了它?
    • @Eiko 问题是一旦字典在下次遇到 optionDataArr 时被删除,它就会出现同样的错误。
    【解决方案2】:

    在这一行以下:

    [optionDataArr removeObjectAtIndex:i];
    

    添加这一行:

    i--;
    

    所以,代码是:

    if (sName.length == 0  && sPrice.length == 0) {
        [optionDataArr removeObjectAtIndex:i];
        i--;
    }
    

    原因:当您在迭代数组时从数组中删除一个项目时,索引会发生变化。因此,这就是您需要手动减少索引的原因。

    【讨论】:

      【解决方案3】:

      Eiko 的答案是正确的,但我想展示另一个使用快速枚举的版本。您无法使用快速枚举删除项目,因此您必须存储索引,然后稍后删除相应的项目:

      NSMutableIndexSet * indexesToRemove = [NSMutableIndexSet indexSet];
      
      [optionDataArr enumerateObjectsUsingBlock:^(NSDictionary *dico, NSUInteger idx, BOOL *stop) {
          if ([dico count] == 0)
              [indexesToRemove addIndex:idx];
      }];
      
      [optionDataArr removeObjectsAtIndexes:indexesToRemove];
      

      编辑:

      正如 Martin R 所建议的,您也可以使用 indexesOfObjectsPassingTestmethod :

      NSIndexSet * indexesToRemove = [optionDataArr indexesOfObjectsPassingTest:^BOOL(NSDictionary *dico, NSUInteger idx, BOOL *stop) {
          return ([dico count] == 0);
      }];
      [optionDataArr removeObjectsAtIndexes:indexesToRemove];
      

      【讨论】:

      • 这可以使用indexesOfObjectsPassingTest:...进一步简化
      • stackoverflow.com/questions/21157109/… 告诉我快速枚举 + mutableArray 比 indexOfObjectsPassingTest 更快。 NSMutableIndexSet 非常接近 NSMutableArray (即使它没有从它继承),所以我的猜测是枚举更快。无论如何,根据情况需要进一步测试。感谢您的建议,我编辑了我的答案以添加它。
      【解决方案4】:

      您当然可以为此使用标准 for 循环,前提是您进行了 Eiko 已经提到的修改。

      然而,在 Objective C 中处理这个问题的惯用方法是遍历数组的副本:

      for (id obj in [optionDataArr copy]) {
          // some processing code
      
          if (condition) {
              [optionDataArr removeObject:obj]
          }
      }
      

      虽然这确实需要数组的副本,但除非您确定要处理大量数据,否则我会从可读版本开始,并在必要时优化到普通的 for 循环。

      【讨论】:

      • 我喜欢惰性代码,但这不是“惯用的”。事实上,这更像是在与语言作斗争。惯用的方式类似于 'NSIndexSet *removals = [optionDataArr indexOfObjectsPassingTest:...]; [optionDataArr removeObjectsAtIndexes:removals];'
      • @Eiko - 对于这个简单的案例,我完全同意你的看法。但在实践中,我倾向于发现我需要对已删除的对象进行一些额外的处理(例如,日志记录)。如果您只有索引,那就没那么容易了。
      • 是的,它可能有效,但我认为如果我们有大数据 @Sapi 无论如何,它的方式很详尽,谢谢您的关注
      猜你喜欢
      • 2019-09-26
      • 1970-01-01
      • 2011-07-20
      • 2020-01-17
      • 2020-12-27
      • 2019-12-05
      • 2013-09-01
      • 1970-01-01
      相关资源
      最近更新 更多