提问人:rustyshelf 提问时间:4/30/2009 最后编辑:Peter Mortensenrustyshelf 更新时间:6/7/2021 访问量:485104
如何对包含自定义对象的 NSMutableArray 进行排序?
How do I sort an NSMutableArray with custom objects in it?
问:
我想做的事情看起来很简单,但我在网上找不到任何答案。我有一个对象,假设它们是“人”对象。我想按 Person.birthDate 排序,这是一个 .NSMutableArray
NSMutableArray
NSDate
我认为这与这种方法有关:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
在 Java 中,我会让我的对象实现 Comparable,或者将 Collections.sort 与内联自定义比较器一起使用......在Objective-C中,你到底是怎么做到的?
答:
你的对象需要实现一个方法,比如说哪个对象接受另一个对象,并根据两个对象之间的关系返回。Person
compare:
Person
NSComparisonResult
然后你会打电话,它应该完成。sortedArrayUsingSelector:
@selector(compare:)
还有其他方法,但据我所知,界面没有 Cocoa-equiv。使用可能是最轻松的方法。Comparable
sortedArrayUsingSelector:
参见 sortUsingFunction:context 方法:
NSMutableArray
您将需要设置一个比较函数,该函数接受两个对象(类型为 ,因为您正在比较两个对象)和一个上下文参数。Person
Person
这两个对象只是 的实例。第三个对象是一个字符串,例如@“birthDate”。Person
此函数返回一个 : 如果 < ,则返回 。如果>,它将返回。最后,它将返回 if == 。NSComparisonResult
NSOrderedAscending
PersonA.birthDate
PersonB.birthDate
NSOrderedDescending
PersonA.birthDate
PersonB.birthDate
NSOrderedSame
PersonA.birthDate
PersonB.birthDate
这是粗略的伪代码;您需要充实一个日期与另一个日期“更少”、“更多”或“相等”的含义(例如比较自纪元以来的秒数等):
NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
if ([firstPerson birthDate] < [secondPerson birthDate])
return NSOrderedAscending;
else if ([firstPerson birthDate] > [secondPerson birthDate])
return NSOrderedDescending;
else
return NSOrderedSame;
}
如果你想要更紧凑的东西,你可以使用三元运算符:
NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}
如果你经常这样做,内联也许可以加快一点速度。
评论
Compare 方法
要么为对象实现 compare-method:
- (NSComparisonResult)compare:(Person *)otherObject {
return [self.birthDate compare:otherObject.birthDate];
}
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
NSSortDescriptor(更好)
或者通常更好:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
您可以通过向数组添加多个键来轻松地按多个键进行排序。也可以使用自定义比较器方法。请查看文档。
方块(闪亮!
从 Mac OS X 10.6 和 iOS 4 开始,还可以使用块进行排序:
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(Person *a, Person *b) {
return [a.birthDate compare:b.birthDate];
}];
性能
一般来说,基于块的方法会比使用的方法快得多,因为后者依赖于 KVC。该方法的主要优点是它提供了一种使用数据而不是代码定义排序顺序的方法,这使得例如设置内容变得容易,以便用户可以通过单击标题行进行排序。-compare:
NSSortDescriptor
NSSortDescriptor
NSTableView
评论
NSMutableArray
sortUsingDescriptors
sortUsingFunction
sortUsingSelector
Georg Schölly 的第二个答案中缺少一个步骤,但效果很好。
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
添加了“s”,因为我在复制和粘贴时浪费了时间,并且在sortedArrayUsingDescriptors中没有“s”的情况下失败了
评论
对于 ,请使用该方法。它对 it-place 进行排序,而不创建新实例。NSMutableArray
sortUsingSelector
评论
sortUsingComparator:
我尝试了所有方法,但这对我有用。在一个类中,我有另一个名为“”的类,并希望按“”的属性进行排序。crimeScene
crimeScene
这就像一个魅力:
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
从 iOS 4 开始,您还可以使用块进行排序。
对于这个特定示例,我假设数组中的对象有一个“position”方法,该方法返回一个 .NSInteger
NSArray *arrayToSort = where ever you get the array from... ;
NSComparisonResult (^sortBlock)(id, id) = ^(id obj1, id obj2)
{
if ([obj1 position] > [obj2 position])
{
return (NSComparisonResult)NSOrderedDescending;
}
if ([obj1 position] < [obj2 position])
{
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
};
NSArray *sorted = [arrayToSort sortedArrayUsingComparator:sortBlock];
注意:“sorted”数组将自动释放。
我在 iOS 4 中使用块执行此操作。 必须将数组的元素从 id 转换为我的类类型。 在本例中,它是一个名为 Score 的类,其属性称为 points。
此外,如果数组的元素不是正确的类型,您还需要决定该怎么做,对于这个示例,我刚刚返回了 ,但是在我的代码中,我遇到了一个异常。NSOrderedSame
NSArray *sorted = [_scores sortedArrayUsingComparator:^(id obj1, id obj2){
if ([obj1 isKindOfClass:[Score class]] && [obj2 isKindOfClass:[Score class]]) {
Score *s1 = obj1;
Score *s2 = obj2;
if (s1.points > s2.points) {
return (NSComparisonResult)NSOrderedAscending;
} else if (s1.points < s2.points) {
return (NSComparisonResult)NSOrderedDescending;
}
}
// TODO: default is the same?
return (NSComparisonResult)NSOrderedSame;
}];
return sorted;
PS:这是按降序排序的。
评论
downcasting
return ((!obj1 && !obj2) ? NSOrderedSame : (obj1 ? NSOrderedAscending : NSOrderedDescending))
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
谢谢,它工作正常...
iOS 4 块将为您节省:)
featuresArray = [[unsortedFeaturesArray sortedArrayUsingComparator: ^(id a, id b)
{
DMSeatFeature *first = ( DMSeatFeature* ) a;
DMSeatFeature *second = ( DMSeatFeature* ) b;
if ( first.quality == second.quality )
return NSOrderedSame;
else
{
if ( eSeatQualityGreen == m_seatQuality || eSeatQualityYellowGreen == m_seatQuality || eSeatQualityDefault == m_seatQuality )
{
if ( first.quality < second.quality )
return NSOrderedAscending;
else
return NSOrderedDescending;
}
else // eSeatQualityRed || eSeatQualityYellow
{
if ( first.quality > second.quality )
return NSOrderedAscending;
else
return NSOrderedDescending;
}
}
}] retain];
http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html 一点描述
我在我的一些项目中使用了sortUsingFunction::
int SortPlays(id a, id b, void* context)
{
Play* p1 = a;
Play* p2 = b;
if (p1.score<p2.score)
return NSOrderedDescending;
else if (p1.score>p2.score)
return NSOrderedAscending;
return NSOrderedSame;
}
...
[validPlays sortUsingFunction:SortPlays context:nil];
排序非常简单:NSMutableArray
NSMutableArray *arrayToFilter =
[[NSMutableArray arrayWithObjects:@"Photoshop",
@"Flex",
@"AIR",
@"Flash",
@"Acrobat", nil] autorelease];
NSMutableArray *productsToRemove = [[NSMutableArray array] autorelease];
for (NSString *products in arrayToFilter) {
if (fliterText &&
[products rangeOfString:fliterText
options:NSLiteralSearch|NSCaseInsensitiveSearch].length == 0)
[productsToRemove addObject:products];
}
[arrayToFilter removeObjectsInArray:productsToRemove];
如果您只是对 的数组进行排序,则可以使用 1 次调用对它们进行排序:NSNumbers
[arrayToSort sortUsingSelector: @selector(compare:)];
这之所以有效,是因为数组中的对象 ( objects) 实现了 compare 方法。您可以对对象执行相同的操作,甚至可以对实现比较方法的自定义数据对象数组执行相同的操作。NSNumber
NSString
下面是一些使用比较器模块的示例代码。它对字典数组进行排序,其中每个字典在键“sort_key”中包含一个数字。
#define SORT_KEY @\"sort_key\"
[anArray sortUsingComparator:
^(id obj1, id obj2)
{
NSInteger value1 = [[obj1 objectForKey: SORT_KEY] intValue];
NSInteger value2 = [[obj2 objectForKey: SORT_KEY] intValue];
if (value1 > value2)
{
return (NSComparisonResult)NSOrderedDescending;
}
if (value1 < value2)
{
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
上面的代码介绍了获取每个排序键的整数值并进行比较的工作,以说明如何做到这一点。由于对象实现了 compare 方法,因此可以更简单地重写它:NSNumber
#define SORT_KEY @\"sort_key\"
[anArray sortUsingComparator:
^(id obj1, id obj2)
{
NSNumber* key1 = [obj1 objectForKey: SORT_KEY];
NSNumber* key2 = [obj2 objectForKey: SORT_KEY];
return [key1 compare: key2];
}];
或者比较器的主体甚至可以提炼成 1 行:
return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
我倾向于使用简单的语句和大量的临时变量,因为代码更易于阅读和调试。无论如何,编译器都会优化掉临时变量,因此多合一版本没有任何优势。
-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted
{
NSArray *sortedArray;
sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
{
return [a compare:b];
}];
return [sortedArray mutableCopy];
}
评论
我创建了一个小型的类别方法库,称为 Linq to ObjectiveC,它使这种事情变得更加容易。使用带有键选择器的 sort 方法,可以按如下方式进行排序:birthDate
NSArray* sortedByBirthDate = [input sort:^id(id person) {
return [person birthDate];
}]
评论
我刚刚根据自定义要求进行了多级排序。
对值进行排序
[arrItem sortUsingComparator:^NSComparisonResult (id a, id b){
ItemDetail * itemA = (ItemDetail*)a;
ItemDetail* itemB =(ItemDetail*)b;
//item price are same
if (itemA.m_price.m_selling== itemB.m_price.m_selling) {
NSComparisonResult result= [itemA.m_itemName compare:itemB.m_itemName];
//if item names are same, then monogramminginfo has to come before the non monograme item
if (result==NSOrderedSame) {
if (itemA.m_monogrammingInfo) {
return NSOrderedAscending;
}else{
return NSOrderedDescending;
}
}
return result;
}
//asscending order
return itemA.m_price.m_selling > itemB.m_price.m_selling;
}];
https://sites.google.com/site/greateindiaclub/mobil-apps/ios/multilevelsortinginiosobjectivec
NSMutableArray *stockHoldingCompanies = [NSMutableArray arrayWithObjects:fortune1stock,fortune2stock,fortune3stock,fortune4stock,fortune5stock,fortune6stock , nil];
NSSortDescriptor *sortOrder = [NSSortDescriptor sortDescriptorWithKey:@"companyName" ascending:NO];
[stockHoldingCompanies sortUsingDescriptors:[NSArray arrayWithObject:sortOrder]];
NSEnumerator *enumerator = [stockHoldingCompanies objectEnumerator];
ForeignStockHolding *stockHoldingCompany;
NSLog(@"Fortune 6 companies sorted by Company Name");
while (stockHoldingCompany = [enumerator nextObject]) {
NSLog(@"===============================");
NSLog(@"CompanyName:%@",stockHoldingCompany.companyName);
NSLog(@"Purchase Share Price:%.2f",stockHoldingCompany.purchaseSharePrice);
NSLog(@"Current Share Price: %.2f",stockHoldingCompany.currentSharePrice);
NSLog(@"Number of Shares: %i",stockHoldingCompany.numberOfShares);
NSLog(@"Cost in Dollars: %.2f",[stockHoldingCompany costInDollars]);
NSLog(@"Value in Dollars : %.2f",[stockHoldingCompany valueInDollars]);
}
NSLog(@"===============================");
您可以根据自己的目的使用以下通用方法。它应该可以解决您的问题。
//Called method
-(NSMutableArray*)sortArrayList:(NSMutableArray*)arrDeviceList filterKeyName:(NSString*)sortKeyName ascending:(BOOL)isAscending{
NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:sortKeyName ascending:isAscending];
[arrDeviceList sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
return arrDeviceList;
}
//Calling method
[self sortArrayList:arrSomeList filterKeyName:@"anything like date,name etc" ascending:YES];
使用 NSComparator 进行排序
如果我们想对自定义对象进行排序,我们需要提供 ,用于比较自定义对象。该块返回一个值来表示两个对象的顺序。因此,为了对整个数组进行排序,以下方式使用。NSComparator
NSComparisonResult
NSComparator
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){
return [e1.firstname compare:e2.firstname];
}];
使用 NSSortDescriptor
进行排序 举个例子,假设我们有一个包含自定义类实例的数组,Employee 具有属性 firstname、lastname 和 age。下面的示例演示如何创建一个 NSSortDescriptor,该描述符可用于按年龄键按升序对数组内容进行排序。
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];
NSArray *sortDescriptors = @[ageDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
使用自定义比较进行排序 名称是字符串,在对字符串进行排序以呈现给用户时,应始终使用本地化比较
。通常,您还希望执行不区分大小写的比较。下面是一个示例,其中包含 (localizedStandardCompare:) 按姓氏和名字对数组进行排序。
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];
NSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];
NSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];
有关参考和详细讨论,请参阅:https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
http://www.ios-blog.co.uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/
就我而言,我使用“sortedArrayUsingComparator”对数组进行排序。请看下面的代码。
contactArray = [[NSArray arrayWithArray:[contactSet allObjects]] sortedArrayUsingComparator:^NSComparisonResult(ContactListData *obj1, ContactListData *obj2) {
NSString *obj1Str = [NSString stringWithFormat:@"%@ %@",obj1.contactName,obj1.contactSurname];
NSString *obj2Str = [NSString stringWithFormat:@"%@ %@",obj2.contactName,obj2.contactSurname];
return [obj1Str compare:obj2Str];
}];
我的目标也是,
@interface ContactListData : JsonData
@property(nonatomic,strong) NSString * contactName;
@property(nonatomic,strong) NSString * contactSurname;
@property(nonatomic,strong) NSString * contactPhoneNumber;
@property(nonatomic) BOOL isSelected;
@end
Swift 的协议和函数式编程使这变得非常容易,你只需要让你的类符合 Comparable 协议,实现协议所需的方法,然后使用 sorted(by: ) 高阶函数创建一个排序数组,而无需顺便使用可变数组。
class Person: Comparable {
var birthDate: NSDate?
let name: String
init(name: String) {
self.name = name
}
static func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.birthDate === rhs.birthDate || lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedSame
}
static func <(lhs: Person, rhs: Person) -> Bool {
return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedAscending
}
static func >(lhs: Person, rhs: Person) -> Bool {
return lhs.birthDate?.compare(rhs.birthDate as! Date) == .orderedDescending
}
}
let p1 = Person(name: "Sasha")
p1.birthDate = NSDate()
let p2 = Person(name: "James")
p2.birthDate = NSDate()//he is older by miliseconds
if p1 == p2 {
print("they are the same") //they are not
}
let persons = [p1, p2]
//sort the array based on who is older
let sortedPersons = persons.sorted(by: {$0 > $1})
//print sasha which is p1
print(persons.first?.name)
//print James which is the "older"
print(sortedPersons.first?.name)
在 Swift 中对数组进行排序
对于以下人员来说,这是一项非常干净的技术,可以在全球范围内实现上述目标。让我们有一个示例自定义类,该类具有一些属性。Swifty
User
class User: NSObject {
var id: String?
var name: String?
var email: String?
var createdDate: Date?
}
现在我们有一个数组,我们需要根据升序和/或降序进行排序。因此,让我们添加一个日期比较函数。createdDate
class User: NSObject {
var id: String?
var name: String?
var email: String?
var createdDate: Date?
func checkForOrder(_ otherUser: User, _ order: ComparisonResult) -> Bool {
if let myCreatedDate = self.createdDate, let othersCreatedDate = otherUser.createdDate {
//This line will compare both date with the order that has been passed.
return myCreatedDate.compare(othersCreatedDate) == order
}
return false
}
}
现在让我们有一个 of .简而言之,让我们只为那些只有对象的数组添加一些方法。extension
Array
User
User
extension Array where Element: User {
//This method only takes an order type. i.e ComparisonResult.orderedAscending
func sortUserByDate(_ order: ComparisonResult) -> [User] {
let sortedArray = self.sorted { (user1, user2) -> Bool in
return user1.checkForOrder(user2, order)
}
return sortedArray
}
}
升序的用法
let sortedArray = someArray.sortUserByDate(.orderedAscending)
降序用法
let sortedArray = someArray.sortUserByDate(.orderedAscending)
同一订单的使用情况
let sortedArray = someArray.sortUserByDate(.orderedSame)
上述方法 in 只有在 类型为 ||
extension
Array
[User]
Array<User>
您必须创建 sortDescriptor,然后可以使用 sortDescriptor 对 nsmutablearray 进行排序,如下所示。
let sortDescriptor = NSSortDescriptor(key: "birthDate", ascending: true, selector: #selector(NSString.compare(_:)))
let array = NSMutableArray(array: self.aryExist.sortedArray(using: [sortDescriptor]))
print(array)
使用 NSSortDescriptor 对具有自定义对象的 NSMutableArray 进行排序
NSSortDescriptor *sortingDescriptor;
sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
ascending:YES];
NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
像这样用于嵌套对象,
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastRoute.to.lastname" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSMutableArray *sortedPackages = [[NSMutableArray alloc]initWithArray:[packages sortedArrayUsingDescriptors:@[sortDescriptor]]];
lastRoute 是一个对象,该对象保存 to 对象,to 对象保存 lastname 字符串值。
Swift版本:5.1
如果您有自定义结构或类并希望对它们进行任意排序,则应使用尾随闭包调用 sort(),该闭包对指定的字段进行排序。下面是一个示例,使用对特定属性进行排序的自定义结构数组:
struct User {
var firstName: String
}
var users = [
User(firstName: "Jemima"),
User(firstName: "Peter"),
User(firstName: "David"),
User(firstName: "Kelly"),
User(firstName: "Isabella")
]
users.sort {
$0.firstName < $1.firstName
}
如果你想返回一个排序的数组,而不是就地排序,请使用sorted(),如下所示:
let sortedUsers = users.sorted {
$0.firstName < $1.firstName
}
let sortedUsers = users.sorted {
$0.firstName < $1.firstName
}
评论