วิธีที่ดีที่สุดในการลบค่าที่ซ้ำกัน ( NSString
) จากNSMutableArray
ใน Objective-C?
นี่เป็นวิธีที่ง่ายที่สุดและถูกต้องใช่ไหม
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
วิธีที่ดีที่สุดในการลบค่าที่ซ้ำกัน ( NSString
) จากNSMutableArray
ใน Objective-C?
นี่เป็นวิธีที่ง่ายที่สุดและถูกต้องใช่ไหม
uniquearray = [[NSSet setWithArray:yourarray] allObjects];
คำตอบ:
NSSet
วิธีการของคุณดีที่สุดถ้าคุณไม่กังวลเกี่ยวกับลำดับของวัตถุ แต่แล้วอีกครั้งหากคุณไม่กังวลเกี่ยวกับการสั่งซื้อแล้วทำไมคุณไม่เก็บไว้ในการNSSet
เริ่มต้นด้วย?
ฉันเขียนคำตอบด้านล่างในปี 2009; ในปี 2011 Apple เพิ่มลงNSOrderedSet
ใน iOS 5 และ Mac OS X 10.7 สิ่งที่เคยเป็นอัลกอริทึมคือตอนนี้โค้ดสองบรรทัด:
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
หากคุณกังวลเกี่ยวกับการสั่งซื้อและคุณใช้งานบน iOS 4 หรือเก่ากว่าให้วนซ้ำชุดข้อมูล:
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];
คุณก็สามารถรับอาร์เรย์กลับมาได้array = [orderedSet allObjects];
หรือเพียงแค่ใช้NSOrderedSet
s แทนการNSArray
ใช้ครั้งแรก
[orderedSet allObjects]
ด้วย[orderedSet array]
!
NSArray
NSMutableArray
ในตัวอย่างของคุณคุณทำงานในทางกลับกัน
NSSet
) หรือลิงค์ @Simon Whitaker ป้องกันก่อนที่จะเพิ่มมูลค่าที่ซ้ำกันซึ่งเป็นวิธีที่มีประสิทธิภาพ?
ฉันรู้ว่านี้เป็นคำถามที่เก่า แต่มีวิธีที่สง่างามมากขึ้นที่จะลบข้อมูลที่ซ้ำกันในถ้าคุณไม่สนใจเกี่ยวกับการสั่งซื้อNSArray
ถ้าเราใช้Object ประกอบจากการเข้ารหัสค่าคีย์เราสามารถทำได้:
uniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
ในฐานะที่เป็นAnthoPakยังตั้งข้อสังเกตว่าเป็นไปได้ที่จะลบรายการที่ซ้ำกันตามคุณสมบัติ ตัวอย่างจะเป็น:@distinctUnionOfObjects.name
@distinctUnionOfObjects.property
การลบรายการที่ซ้ำกันโดยคุณสมบัติของอาร์เรย์ของวัตถุที่กำหนดเอง ตัวอย่างเช่น@distinctUnionOfObjects.name
ใช่การใช้ NSSet เป็นแนวทางที่สมเหตุสมผล
หากต้องการเพิ่มคำตอบของ Jim Puls ต่อไปนี้เป็นวิธีการอื่นในการลอกรายการที่ซ้ำกันในขณะที่รักษาลำดับ:
// Initialise a new, empty mutable array
NSMutableArray *unique = [NSMutableArray array];
for (id obj in originalArray) {
if (![unique containsObject:obj]) {
[unique addObject:obj];
}
}
โดยพื้นฐานแล้วมันเป็นวิธีการเดียวกับของ Jim แต่คัดลอกรายการที่ไม่ซ้ำกันไปยังอาร์เรย์ที่ไม่แน่นอนซึ่งสดใหม่แทนที่จะลบรายการที่ซ้ำจากต้นฉบับ ทำให้หน่วยความจำมีประสิทธิภาพมากขึ้นเล็กน้อยในกรณีของอาเรย์ขนาดใหญ่ที่มีการซ้ำซ้อนจำนวนมาก (ไม่จำเป็นต้องทำสำเนาของทั้งอาเรย์) และฉันคิดว่าสามารถอ่านได้อีกเล็กน้อย
โปรดทราบว่าในกรณีใดกรณีหนึ่งการตรวจสอบเพื่อดูว่ามีรายการรวมอยู่ในอาร์เรย์เป้าหมาย (ใช้containsObject:
ในตัวอย่างของฉันหรือindexOfObject:inRange:
ใน Jim's) ไม่ได้ปรับขนาดสำหรับอาร์เรย์ขนาดใหญ่ การตรวจสอบเหล่านั้นทำงานในเวลา O (N) ซึ่งหมายความว่าหากคุณเพิ่มขนาดของอาร์เรย์เดิมเป็นสองเท่าการตรวจสอบแต่ละครั้งจะใช้เวลานานเป็นสองเท่าในการเรียกใช้ เมื่อคุณทำการตรวจสอบสำหรับแต่ละวัตถุในอาร์เรย์คุณจะต้องเรียกใช้การตรวจสอบที่มีราคาแพงกว่า อัลกอริทึมโดยรวม (ทั้งของฉันและของจิม) ทำงานในเวลา O (N 2 ) ซึ่งมีราคาสูงขึ้นอย่างรวดเร็วเมื่ออาเรย์ดั้งเดิมเติบโตขึ้น
เพื่อให้ได้เวลานั้นถึง O (N) คุณสามารถใช้ a NSMutableSet
เพื่อเก็บบันทึกรายการที่ถูกเพิ่มไปยังอาร์เรย์ใหม่เนื่องจากการค้นหา NSSet เป็น O (1) แทนที่จะเป็น O (N) กล่าวอีกนัยหนึ่งการตรวจสอบเพื่อดูว่าองค์ประกอบเป็นสมาชิกของ NSSet ใช้เวลาเดียวกันโดยไม่คำนึงถึงจำนวนองค์ประกอบที่อยู่ในชุด
โค้ดที่ใช้วิธีการนี้จะมีลักษณะดังนี้:
NSMutableArray *unique = [NSMutableArray array];
NSMutableSet *seen = [NSMutableSet set];
for (id obj in originalArray) {
if (![seen containsObject:obj]) {
[unique addObject:obj];
[seen addObject:obj];
}
}
เรื่องนี้ดูเหมือนว่าจะสิ้นเปลืองเพียงเล็กน้อย เรายังคงสร้างอาเรย์ใหม่เมื่อคำถามชัดเจนว่าอาเรย์เดิมนั้นไม่แน่นอนดังนั้นเราควรจะสามารถแยกมันออกมาและบันทึกหน่วยความจำบางส่วนได้ บางสิ่งเช่นนี้
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++;
}
}
UPDATE : Yuri Niyazov ชี้ให้เห็นว่าคำตอบสุดท้ายของฉันทำงานจริงใน O (N 2 ) เพราะremoveObjectAtIndex:
อาจทำงานในเวลา O (N)
(เขาบอกว่า "อาจจะ" เพราะเราไม่รู้ว่ามันถูกนำไปใช้อย่างไร แต่สิ่งหนึ่งที่เป็นไปได้คือการนำไปใช้หลังจากการลบวัตถุที่ index X วิธีนั้นจะวนซ้ำทุกองค์ประกอบจากดัชนี X + 1 ไปยังวัตถุสุดท้ายในอาร์เรย์ ให้ย้ายพวกมันไปยังดัชนีก่อนหน้าถ้าเป็นเช่นนั้นประสิทธิภาพ O (N) จะเป็นจริง)
แล้วจะทำอย่างไรดี? มันขึ้นอยู่กับสถานการณ์ หากคุณมีอาร์เรย์จำนวนมากและคุณคาดหวังว่าจะมีจำนวนรายการที่ซ้ำกันเพียงเล็กน้อยการยกเลิกการทำซ้ำแบบแทนที่จะทำงานได้ดีและช่วยให้คุณไม่ต้องสร้างอาร์เรย์ที่ซ้ำกัน หากคุณมีอาเรย์ที่คุณคาดหวังว่าจะมีการซ้ำซ้อนจำนวนมากจากนั้นสร้างอาเรย์แยกต่างหากอาเรย์ที่ซ้ำกันอาจเป็นวิธีที่ดีที่สุด สิ่งที่ต้องทำในที่นี้คือสัญกรณ์ใหญ่ ๆ O อธิบายเฉพาะคุณลักษณะของอัลกอริธึมเท่านั้นมันจะไม่บอกคุณอย่างแน่นอนว่าดีที่สุดสำหรับสถานการณ์ใดก็ตาม
หากคุณกำหนดเป้าหมาย iOS 5+ (สิ่งที่ครอบคลุมโลกทั้ง iOS), NSOrderedSet
การใช้งานที่ดีที่สุด NSArray
มันเอารายการที่ซ้ำกันและยังคงรักษาคำสั่งของคุณ
แค่ทำ
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourArray];
ตอนนี้คุณสามารถแปลงกลับเป็น NSArray ที่เป็นเอกลักษณ์
NSArray *uniqueArray = orderedSet.array;
หรือเพียงแค่ใช้ orderedSet เพราะมีวิธีการเดียวกันเช่น NSArray เหมือนobjectAtIndex:
, firstObject
และอื่น ๆ
การตรวจสอบการเป็นสมาชิกcontains
นั้นยิ่งเร็วNSOrderedSet
กว่าที่เป็นอยู่NSArray
สำหรับการชำระเงินเพิ่มเติมการอ้างอิงชุด NSOrdered
พร้อมใช้งานใน OS X v10.7 และใหม่กว่า
หากคุณกังวลเกี่ยวกับการสั่งซื้อวิธีที่ถูกต้องที่จะทำ
NSArray *no = [[NSOrderedSet orderedSetWithArray:originalArray]allObjects];
นี่คือรหัสการลบค่าที่ซ้ำกันออกจาก NSArray ในการสั่งซื้อ
ต้องการสั่งซื้อ
NSArray *yourarray = @[@"a",@"b",@"c"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:yourarray];
NSArray *arrayWithoutDuplicates = [orderedSet array];
NSLog(@"%@",arrayWithoutDuplicates);
หรือไม่ต้องการคำสั่ง
NSSet *set = [NSSet setWithArray:yourarray];
NSArray *arrayWithoutOrder = [set allObjects];
NSLog(@"%@",arrayWithoutOrder);
ที่นี่ฉันลบค่าชื่อที่ซ้ำกันจาก mainArray และเก็บผลใน 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]];
}
}
โปรดทราบว่าหากคุณมีอาร์เรย์ที่เรียงลำดับคุณไม่จำเป็นต้องตรวจสอบรายการอื่น ๆ ในอาร์เรย์เพียงรายการสุดท้าย ควรเร็วกว่าการตรวจสอบกับรายการทั้งหมด
// 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]];
}
}
ดูเหมือนว่า NSOrderedSet
คำตอบที่แนะนำยังต้องใช้รหัสน้อยกว่ามาก แต่ถ้าคุณไม่สามารถใช้NSOrderedSet
ด้วยเหตุผลบางอย่างและคุณมีอาร์เรย์ที่เรียงลำดับฉันเชื่อว่าโซลูชันของฉันจะเร็วที่สุด ฉันไม่แน่ใจว่ามันเปรียบเทียบกับความเร็วของNSOrderedSet
โซลูชั่นได้อย่างไร นอกจากนี้ทราบว่ารหัสของฉันคือการตรวจสอบด้วยดังนั้นชุดเดียวกันของตัวอักษรจะไม่ปรากฏมากกว่าหนึ่งครั้งในisEqualToString:
newArray
ฉันไม่แน่ใจว่าNSOrderedSet
โซลูชันจะลบรายการที่ซ้ำกันตามมูลค่าหรือตามตำแหน่งหน่วยความจำ
ตัวอย่างของฉันสมมติว่าsortedSourceArray
มีเพียงNSString
แค่แค่NSMutableString
s หรือผสมของทั้งสอง หากsortedSourceArray
มีเพียงNSNumber
s หรือเพียงNSDate
s แทนคุณสามารถแทนที่
if (![[sortedSourceArray objectAtIndex:i] isEqualToString:[sortedSourceArray objectAtIndex:(i-1)]])
กับ
if ([[sortedSourceArray objectAtIndex:i] compare:[sortedSourceArray objectAtIndex:(i-1)]] != NSOrderedSame)
และมันควรจะทำงานได้อย่างสมบูรณ์แบบ หากsortedSourceArray
มีการผสมผสานของNSString
s, NSNumber
s และ / หรือNSDate
s มันอาจจะผิดพลาด
มีผู้ประกอบการวัตถุ KVC ที่นำเสนอโซลูชั่นที่สง่างามมากขึ้นเป็นuniquearray = [yourarray valueForKeyPath:@"@distinctUnionOfObjects.self"];
นี่คือหมวด NSArray
อีกวิธีหนึ่งที่ง่ายขึ้นคุณสามารถลองใช้ซึ่งจะไม่เพิ่มค่าซ้ำกันก่อนที่จะเพิ่มวัตถุในอาร์เรย์: -
// สมมติว่า mutableArray ได้รับการจัดสรรและเริ่มต้นและมีค่าบางอย่าง
if (![yourMutableArray containsObject:someValue])
{
[yourMutableArray addObject:someValue];
}
ลบค่าที่ซ้ำกันออกจาก NSMutableArray ใน Objective-C
NSMutableArray *datelistArray = [[NSMutableArray alloc]init];
for (Student * data in fetchStudentDateArray)
{
if([datelistArray indexOfObject:data.date] == NSNotFound)
[datelistArray addObject:data.date];
}
นี่คือรหัสการลบค่าที่ซ้ำกันออกจาก NSMutable Array .มันจะทำงานสำหรับคุณ. myArray เป็น Array Mutable ที่คุณต้องการลบค่าที่ซ้ำกัน
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
การใช้Orderedset
จะทำเคล็ดลับ วิธีนี้จะทำให้การลบรายการซ้ำออกจากอาร์เรย์และคงไว้ซึ่งลำดับที่ชุดปกติไม่ได้ทำ
เพียงใช้รหัสง่ายๆนี้:
NSArray *hasDuplicates = /* (...) */;
NSArray *noDuplicates = [[NSSet setWithArray: hasDuplicates] allObjects];
เนื่องจาก nsset ไม่อนุญาตให้มีค่าซ้ำกันและวัตถุทั้งหมดส่งคืนอาร์เรย์
NSOrderedSet
insteed NSSet
ของ