Cách tốt nhất để xóa các giá trị trùng lặp ( NSString
) khỏi NSMutableArray
trong Objective-C?
Đây có phải là cách dễ nhất và đúng để làm điều đó?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Cách tốt nhất để xóa các giá trị trùng lặp ( NSString
) khỏi NSMutableArray
trong Objective-C?
Đây có phải là cách dễ nhất và đúng để làm điều đó?
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
Câu trả lời:
NSSet
Cách tiếp cận của bạn là tốt nhất nếu bạn không lo lắng về thứ tự của các đối tượng, nhưng một lần nữa, nếu bạn không lo lắng về thứ tự, vậy tại sao bạn không lưu trữ chúng trong mộtNSSet
để bắt đầu?
Tôi đã viết câu trả lời dưới đây vào năm 2009; vào năm 2011, Apple đã thêm NSOrderedSet
vào iOS 5 và Mac OS X 10.7. Những gì đã là một thuật toán bây giờ là hai dòng mã:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
Nếu bạn lo lắng về thứ tự và bạn đang chạy trên iOS 4 trở về trước, hãy lặp lại một bản sao của mảng:
NSArray *copy = [mutableArray copy];
NSInteger index = [copy count] - 1;
for (id object in [copy reverseObjectEnumerator]) {
if ([mutableArray indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound) {
[mutableArray removeObjectAtIndex:index];
}
index--;
}
[copy release];
[NSOrderedSet orderedSetWithArray:array];
Bạn có thể lấy lại một mảng thông qua array = [orderedSet allObjects];
hoặc chỉ sử dụng NSOrderedSet
s thay vì NSArray
ở vị trí đầu tiên.
[orderedSet allObjects]
bằng [orderedSet array]
!
NSArray
và nên tạo temp NSMutableArray
. Trong ví dụ của bạn, bạn làm việc ngược lại
NSSet
) hoặc liên kết @Simon Whitaker ngăn chặn trước khi thêm giá trị trùng lặp đó là cách hiệu quả?
Tôi biết đây là một câu hỏi cũ, nhưng có một cách thanh lịch hơn để loại bỏ các bản sao trong một NSArray
nếu bạn không quan tâm đến thứ tự .
Nếu chúng tôi sử dụng Toán tử đối tượng từ Mã hóa giá trị khóa, chúng tôi có thể thực hiện việc này:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Như AnthoPak cũng lưu ý rằng có thể loại bỏ các bản sao dựa trên một tài sản. Một ví dụ sẽ là:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
để loại bỏ các bản sao theo thuộc tính của một mảng các đối tượng tùy chỉnh. Ví dụ@distinctUnionOfObjects.name
Có, sử dụng NSSet là một cách tiếp cận hợp lý.
Để thêm vào câu trả lời của Jim Puls, đây là một cách tiếp cận khác để tước bỏ các bản sao trong khi vẫn giữ trật tự:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
Về cơ bản, đó là cách tiếp cận tương tự như của Jim nhưng sao chép các mục độc đáo vào một mảng có thể thay đổi mới thay vì xóa các bản sao khỏi bản gốc. Điều này làm cho bộ nhớ hiệu quả hơn một chút trong trường hợp một mảng lớn có nhiều bản sao (không cần tạo một bản sao của toàn bộ mảng), và theo tôi thì dễ đọc hơn một chút.
Lưu ý rằng trong cả hai trường hợp, kiểm tra xem liệu một mục đã được bao gồm trong mảng mục tiêu (sử dụng containsObject:
trong ví dụ của tôi hay indexOfObject:inRange:
trong Jim) không mở rộng tốt cho các mảng lớn. Các kiểm tra đó chạy trong thời gian O (N), nghĩa là nếu bạn tăng gấp đôi kích thước của mảng ban đầu thì mỗi kiểm tra sẽ mất gấp đôi thời gian để chạy. Vì bạn đang thực hiện kiểm tra cho từng đối tượng trong mảng, nên bạn cũng sẽ chạy nhiều hơn những kiểm tra đắt tiền hơn. Thuật toán tổng thể (cả của tôi và của Jim) chạy trong thời gian O (N 2 ), sẽ nhanh chóng trở nên đắt đỏ khi mảng ban đầu phát triển.
Để giảm thời gian O (N), bạn có thể sử dụng NSMutableSet
để lưu bản ghi các mục đã được thêm vào mảng mới, vì tra cứu NSSet là O (1) thay vì O (N). Nói cách khác, kiểm tra xem liệu một phần tử có phải là thành viên của NSSet có cùng thời gian hay không bất kể có bao nhiêu phần tử trong tập hợp.
Mã sử dụng phương pháp này sẽ trông giống như thế này:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
Điều này vẫn có vẻ hơi lãng phí mặc dù; chúng tôi vẫn đang tạo ra một mảng mới khi câu hỏi làm rõ rằng mảng ban đầu có thể thay đổi được, vì vậy chúng tôi sẽ có thể khử nó tại chỗ và tiết kiệm một số bộ nhớ. Một cái gì đó như thế này:
NSMutableSet *seen = [NSMutableSet set];
NSUInteger i = 0;
while (i < [originalArray count]) {
id obj = [originalArray objectAtIndex:i];
if ([seen containsObject:obj]) {
[originalArray removeObjectAtIndex:i];
// NB: we *don't* increment i here; since
// we've removed the object previously at
// index i, [originalArray objectAtIndex:i]
// now points to the next object in the array.
} else {
[seen addObject:obj];
i++;
}
}
CẬP NHẬT : Yuri Niyazov chỉ ra rằng câu trả lời cuối cùng của tôi thực sự chạy trong O (N 2 ) vì removeObjectAtIndex:
có lẽ chạy trong thời gian O (N).
. , chuyển chúng sang chỉ mục trước. Nếu đó là trường hợp thì đó thực sự là hiệu suất O (N).)
Vậy lam gi? Nó phụ thuộc vào tình hình. Nếu bạn đã có một mảng lớn và bạn chỉ mong đợi một số lượng nhỏ các bản sao thì việc sao chép tại chỗ sẽ hoạt động tốt và tiết kiệm cho bạn khi phải xây dựng một mảng trùng lặp. Nếu bạn đã có một mảng mà bạn mong đợi nhiều bản sao thì việc xây dựng một mảng tách riêng, tách rời có lẽ là cách tiếp cận tốt nhất. Điều đáng nói ở đây là ký hiệu big-O chỉ mô tả các đặc điểm của thuật toán, nó sẽ không cho bạn biết chắc chắn điều gì là tốt nhất cho bất kỳ trường hợp cụ thể nào.
Nếu bạn đang nhắm mục tiêu iOS 5+ (bao gồm toàn bộ thế giới iOS), hãy sử dụng tốt nhất NSOrderedSet
. Nó loại bỏ trùng lặp và giữ lại thứ tự của bạnNSArray
.
Cứ làm đi
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
Bây giờ bạn có thể chuyển đổi nó trở lại thành một NSArray duy nhất
NSArray *uniqueArray = orderedSet.array;
Hoặc chỉ sử dụng order set vì nó có cùng phương thức như NSArray objectAtIndex:
, firstObject
v.v.
Kiểm tra tư cách thành viên contains
thậm chí còn nhanh NSOrderedSet
hơn so với trênNSArray
Để kiểm tra thêm, tham khảo NSOrderedset
cần đặt hàng
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
hoặc không cần đặt hàng
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
Ở đây tôi đã loại bỏ các giá trị tên trùng lặp khỏi mainArray và lưu trữ kết quả trong NSMutableArray (listOfUsers)
for (int i=0; i<mainArray.count; i++) {
if (listOfUsers.count==0) {
[listOfUsers addObject:[mainArray objectAtIndex:i]];
}
else if ([[listOfUsers valueForKey:@"name" ] containsObject:[[mainArray objectAtIndex:i] valueForKey:@"name"]])
{
NSLog(@"Same object");
}
else
{
[listOfUsers addObject:[mainArray objectAtIndex:i]];
}
}
Lưu ý rằng nếu bạn có một mảng được sắp xếp, bạn không cần phải kiểm tra đối với mọi mục khác trong mảng, chỉ mục cuối cùng. Điều này sẽ nhanh hơn nhiều so với việc kiểm tra đối với tất cả các mục.
// sortedSourceArray is the source array, already sorted
NSMutableArray *newArray = [[NSMutableArray alloc] initWithObjects:[sortedSourceArray objectAtIndex:0]];
for (int i = 1; i < [sortedSourceArray count]; i++)
{
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
{
[newArray addObject:[tempArray objectAtIndex:i]];
}
}
Có vẻ như các NSOrderedSet
câu trả lời cũng được đề xuất yêu cầu ít mã hơn, nhưng nếu bạn không thể sử dụng một NSOrderedSet
lý do nào đó và bạn có một mảng được sắp xếp, tôi tin rằng giải pháp của tôi sẽ là nhanh nhất. Tôi không chắc nó so sánh với tốc độ của các NSOrderedSet
giải pháp như thế nào . Cũng lưu ý rằng mã của tôi đang kiểm tra isEqualToString:
, do đó, một loạt các chữ cái sẽ không xuất hiện nhiều hơn một lần newArray
. Tôi không chắc liệu các NSOrderedSet
giải pháp sẽ loại bỏ trùng lặp dựa trên giá trị hoặc dựa trên vị trí bộ nhớ.
Ví dụ của tôi giả sử sortedSourceArray
chỉ chứa NSString
s, chỉNSMutableString
s hoặc kết hợp cả hai. Nếu sortedSourceArray
thay vào đó chỉ chứa NSNumber
s hoặc chỉ NSDate
s, bạn có thể thay thế
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
với
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
và nó sẽ hoạt động hoàn hảo. Nếu sortedSourceArray
chứa hỗn hợp NSString
s, NSNumber
s và / hoặc NSDate
s, nó có thể sẽ bị sập.
Có một toán tử đối tượng KVC cung cấp một giải pháp thanh lịch hơn uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
Đây là một thể loại NSArray .
Một cách đơn giản hơn mà bạn có thể thử sẽ không thêm Giá trị trùng lặp trước khi thêm đối tượng vào mảng: -
// Giả sử mutableArray được phân bổ và khởi tạo và chứa một số giá trị
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
Xóa các giá trị trùng lặp khỏi NSMutableArray trong Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
Đây là mã loại bỏ các giá trị trùng lặp khỏi NSMutable Array. .it sẽ làm việc cho bạn. myArray là Mảng Mutable của bạn mà bạn muốn xóa các giá trị trùng lặp ..
for(int j = 0; j < [myMutableArray count]; j++){
for( k = j+1;k < [myMutableArray count];k++){
NSString *str1 = [myMutableArray objectAtIndex:j];
NSString *str2 = [myMutableArray objectAtIndex:k];
if([str1 isEqualToString:str2])
[myMutableArray removeObjectAtIndex:k];
}
} // Now print your array and will see there is no repeated value
chỉ cần sử dụng mã đơn giản này:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
vì nsset không cho phép các giá trị trùng lặp và tất cả các đối tượng trả về một mảng
NSOrderedSet
insteed của NSSet
.