12

Tôi đang sử dụng khối dựa liệt kê tương tự như đoạn mã sau:Làm thế nào để loại bỏ các phần tử trong NSMutableArray hoặc NSMutableDictionary trong quá trình đếm?

[[[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType] 
    enumerateObjectsWithOptions:NSEnumerationConcurrent 
        usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) { 
// block code here 
}] 

Tôi muốn loại bỏ một số đối tượng trong quá trình liệt kê phụ thuộc vào các giá trị đối tượng của họ.

Tôi làm cách nào để thực hiện việc này? Tôi biết rằng thao tác một mảng hoặc từ điển có thể thay đổi (NSMutableArray hoặc NSMutableDictionary) trong quá trình đếm thường không thể thực hiện được.

Cách tốt nhất để thực hiện điều này là gì?

Cảm ơn bạn!

Trả lời

38

Vì bạn không thể xóa đối tượng khỏi mảng hoặc từ điển trong quá trình liệt kê, bạn sẽ phải tích lũy các mục bạn muốn xóa, sau đó xóa tất cả sau khi liệt kê.

Nếu bạn đang làm việc với một mảng, bạn chỉ có thể tích lũy các chỉ số .:

NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet]; 
NSUInteger currentIndex = 0; 

for (id obj in yourArray) { 
    //do stuff with obj 
    if (shouldBeDeleted(obj)) { 
     [indexesToDelete addIndex:currentIndex]; 
    } 
    currentIndex++; 
} 

[yourArray removeObjectsAtIndexes:indexesToDelete]; 

Kể từ thứ tự của các phím trong một NSDictionary là undefined, cho một NSMutableDictionary bạn sẽ phải tích lũy phím thay vào đó:

NSMutableArray *keysToDelete = [NSMutableArray array]; 

for (id obj in [yourDictionary keyEnumerator]) { 
    //do stuff with obj 
    if (shouldBeDeleted(obj)) { 
     [keysToDelete addObject:obj]; 
    } 
} 

[yourDictionary removeObjectsForKeys:keysToDelete]; 

Điều tương tự nếu bạn đang liệt kê một khối. Khai báo điều tra viên trong cùng một phạm vi mà bạn khai báo khối và nó sẽ được giữ lại và chỉ hoạt động.

Cũng đáng xem câu hỏi này từ 3 năm trước: Best way to remove from NSMutableArray while iterating?.

+0

Cảm ơn bạn, Yuji! Bạn đã giúp tôi rất nhiều. – AlexR

1

Một tùy chọn khác, với một mảng, là sử dụng vòng lặp thông thường for, với giới hạn là count của mảng. Sau đó, người ta cần phải nhận thức được liệu một phần tử có bị loại bỏ khỏi một vị trí không (<= chỉ mục).

Đối với từ điển, trước tiên bạn có thể tạo một mảng với allKeys và sau đó lặp lại qua mảng. Trong trường hợp này, không yêu cầu giá trị chỉ mục.

7

Cho dù bạn xây dựng một chỉ số được đặt ra trong điều tra, hoặc sửa đổi các mảng chính nó trong liệt kê, bạn sẽ phải từ bỏ NSEnumerationConcurrent, vì hầu hết các đối tượng Cocoa không thể an toàn được sửa đổi đồng thời từ nhiều chủ đề.

Dù sao, cách tiếp cận đơn giản nhất (nhưng có thể không hiệu quả nhất) là chỉ liệt kê một bản sao của vùng chứa.

Đối với mảng, bạn có thể liệt kê một bản sao ở mặt sau. Tôi giả định rằng khi mỗi mục đang được liệt kê, bạn có thể quyết định xóa mục đó, nhưng không phải mục khác được liệt kê trước đó hoặc chưa được liệt kê.

NSMutableArray *array = [[rows objectForKey:self.company.coaTypeCode] objectForKey:statementType]; 
[[array copy] enumerateObjectsWithOptions: NSEnumerationReverse 
    usingBlock:^(id coaItem, NSUInteger idx, BOOL *stop) { 
    if ([self objectIsTooUglyToExist:coaItem]) 
     [array removeObjectAtIndex:idx]; 
}] 

Bạn phải liệt kê mảng ngược lại để tránh thay đổi phần chưa được liệt kê của mảng.

Đối với một cuốn từ điển, bạn chỉ có thể liệt kê một bản sao không có tùy chọn đặc biệt:

NSMutableDictionary *dictionary = someDictionary; 
[[dictionary copy] enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 
    if ([self object:obj isTooUglyToExistAtKey:key]) 
     [dictionary removeObjectForKey:key]; 
}]; 
+0

Cảm ơn bạn đã gợi ý về tùy chọn "NSEnumerationConcurrent", Rob! – AlexR

Các vấn đề liên quan