如何使用自定义对象对NSMutableArray进行排序?

rus*_*elf 1253 sorting cocoa-touch objective-c nsmutablearray ios

我想做的事情似乎很简单,但我在网上找不到任何答案.我有一个NSMutableArray对象,让我们说它们是'人'对象.我想NSMutableArray按Person.birthDate 排序,这是一个NSDate.

我认为这与这个方法有关:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(???)];
Run Code Online (Sandbox Code Playgroud)

在Java中,我会使我的对象实现Comparable,或者使用带有内联自定义比较器的Collections.sort ...你到底如何在Objective-C中执行此操作?

Geo*_*lly 2289

比较方法

要么为对象实现compare方法:

- (NSComparisonResult)compare:(Person *)otherObject {
    return [self.birthDate compare:otherObject.birthDate];
}

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
Run Code Online (Sandbox Code Playgroud)

NSSortDescriptor(更好)

或者通常更好:

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                           ascending:YES];
NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Run Code Online (Sandbox Code Playgroud)

通过向阵列添加多个键,您可以轻松地按多个键进行排序.也可以使用自定义比较器方法.看看文档.

块(有光泽!)

从Mac OS X 10.6和iOS 4开始,还可以使用块进行排序:

NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person*)a birthDate];
    NSDate *second = [(Person*)b birthDate];
    return [first compare:second];
}];
Run Code Online (Sandbox Code Playgroud)

性能

-compare:和基于块的方法将是相当快一点,一般来讲,除了使用NSSortDescriptor作为后者依赖于KVC.该NSSortDescriptor方法的主要优点是它提供了一种使用数据而不是代码来定义排序顺序的方法,这使得设置起来很容易,因此用户可以NSTableView通过单击标题行来对其进行排序.

  • @Martin:谢谢!有趣的是,在我获得75票之前没有人注意到. (90认同)
  • 因为这是可接受的答案,因此可能被大多数用户认为是确定的,所以添加第3个基于块的示例可能会有所帮助,以便用户也知道它也存在. (76认同)
  • 第一个示例有一个错误:您将一个对象中的birthDate实例变量与另一个对象本身进行比较,而不是其birthDate变量. (65认同)
  • 如果你有一个`NSMutableArray`我更喜欢使用方法`sortUsingDescriptors`,`sortUsingFunction`或`sortUsingSelector`.只要数组是可变的,我通常不需要排序副本. (11认同)
  • @ orange80:我试过了.我不再拥有Mac了,所以如果你能查看代码会很好. (6认同)
  • 您还可以使用正确的类型声明块的参数,将排序减少到:[drinkDetails sortedArrayUsingComparator:^(Person*a,Person*b){return [a.birthDate compare:b.birthDate]; }] (4认同)

Ale*_*lds 107

NSMutableArray方法sortUsingFunction:context:

您需要设置一个比较函数,它接受两个对象(类型Person,因为您要比较两个Person对象)和一个上下文参数.

这两个对象只是实例Person.第三个对象是一个字符串,例如@"birthDate".

此函数返回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;
}
Run Code Online (Sandbox Code Playgroud)

如果你想要更紧凑的东西,你可以使用三元运算符:

NSComparisonResult compare(Person *firstPerson, Person *secondPerson, void *context) {
  return ([firstPerson birthDate] < [secondPerson birthDate]) ? NSOrderedAscending : ([firstPerson birthDate] > [secondPerson birthDate]) ? NSOrderedDescending : NSOrderedSame;
}
Run Code Online (Sandbox Code Playgroud)

如果你这么做的话,内联也许可以加快一点.

  • 它没有什么不妥,但我认为现在有更好的选择. (12认同)
  • "c-ish"方法有什么问题?它工作正常. (10认同)
  • 使用sortUsingFunction:context:可能是最常用的方式,绝对是最难以理解的方式. (9认同)
  • 也许,但我认为对于那些可能正在寻找类似于Java的抽象Comparator类(类似于obj1,类型obj2)的人来说,Java背景中的某些人可读性不高. (6认同)
  • 我理解你们有几个人正在寻找批评这个完美答案的任何理由,即使这种批评具有很少的技术价值.奇怪的. (5认同)

Chr*_*ris 61

我在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;
Run Code Online (Sandbox Code Playgroud)

PS:这是按降序排序.

  • 你实际上并不需要"(得分*)"投射,你可以做"得分*s1 = obj1;" 因为id会很乐意在没有编译器警告的情况下投射到任何东西:-) (6认同)

lev*_*han 28

从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];
Run Code Online (Sandbox Code Playgroud)

注意:"已排序"数组将自动释放.


Fer*_*ndo 25

我尝试了所有,但这对我有用.在一个类中,我有另一个名为" crimeScene"的类,并希望按" crimeScene" 的属性排序.

这就像一个魅力:

NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
[self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
Run Code Online (Sandbox Code Playgroud)


Man*_*ler 19

GeorgSchölly的第二个答案中缺少一步,但它的工作正常.

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                              ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptor:sortDescriptors];
Run Code Online (Sandbox Code Playgroud)


小智 18

NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [drinkDetails sortedArrayUsingDescriptors:sortDescriptors];
Run Code Online (Sandbox Code Playgroud)

谢谢,它工作得很好......


fre*_*ace 16

您的Person对象需要实现一个方法,比如compare:哪个方法接受另一个Person对象,并NSComparisonResult根据两个对象之间的关系返回.

然后你会打电话sortedArrayUsingSelector:,@selector(compare:)它应该完成.

还有其他方法,但据我所知,没有Cocoa-equiv的Comparable接口.使用sortedArrayUsingSelector:可能是最无痛的方法.


Kos*_*kyi 8

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];
Run Code Online (Sandbox Code Playgroud)

http://sokol8.blogspot.com/2011/04/sorting-nsarray-with-blocks.html有点描述


Den*_*Man 7

对于NSMutableArray,使用该sortUsingSelector方法.它在不创建新实例的情况下对其进行排序.


Arv*_*tel 7

您使用 NSSortDescriptor 对带有自定义对象的 NSMutableArray 进行排序

 NSSortDescriptor *sortingDescriptor;
 sortingDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                       ascending:YES];
 NSArray *sortArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
Run Code Online (Sandbox Code Playgroud)


roo*_*ell 6

我在我的一些项目中使用了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];
Run Code Online (Sandbox Code Playgroud)


Emi*_*tar 6

-(NSMutableArray*) sortArray:(NSMutableArray *)toBeSorted 
{
  NSArray *sortedArray;
  sortedArray = [toBeSorted sortedArrayUsingComparator:^NSComparisonResult(id a, id b) 
  {
    return [a compare:b];
 }];
 return [sortedArray mutableCopy];
}
Run Code Online (Sandbox Code Playgroud)


Col*_*inE 6

我创建了一个名为Linq to ObjectiveC的小型类别方法库,它使此类事情变得更加容易。使用带有键选择器的sort方法,您可以按birthDate如下方式进行排序:

NSArray* sortedByBirthDate = [input sort:^id(id person) {
    return [person birthDate];
}]
Run Code Online (Sandbox Code Playgroud)


Boo*_*lan 6

我刚刚根据自定义要求进行了多级排序。

//对值进行排序

    [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;
}];
Run Code Online (Sandbox Code Playgroud)

https://sites.google.com/site/greateindiaclub/mobil-apps/ios/multilevelsortinginiosobjectivec


MD *_*ari 6

您可以使用以下通用方法.它应该解决你的问题.

//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];
Run Code Online (Sandbox Code Playgroud)


Moh*_*hit 5

排序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];
Run Code Online (Sandbox Code Playgroud)


Din*_*sh_ 5

如果您只是对数组进行NSNumbers排序,可以使用1次调用对它们进行排序:

[arrayToSort sortUsingSelector: @selector(compare:)];
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为数组(NSNumber对象)中的对象实现了compare方法.您可以对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;
 }];
Run Code Online (Sandbox Code Playgroud)

上面的代码通过为每个排序键获取整数值并比较它们的工作,作为如何执行它的说明.由于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];
 }];
Run Code Online (Sandbox Code Playgroud)

或者比较器的主体甚至可以蒸馏到1行:

  return [[obj1 objectForKey: SORT_KEY] compare: [obj2 objectForKey: SORT_KEY]];
Run Code Online (Sandbox Code Playgroud)

我倾向于喜欢简单的语句和许多临时变量,因为代码更容易阅读,更容易调试.无论如何,编译器都会优化临时变量,因此对于一体化版本没有任何优势.


Aam*_*mir 5

使用 NSComparator 排序

\n\n

如果我们想对自定义对象进行排序,我们需要提供NSComparator,它用于比较自定义对象。该块返回一个NSComparisonResult值来表示两个对象的顺序。因此,为了对整个数组进行排序,NSComparator请按以下方式使用。

\n\n
NSArray *sortedArray = [employeesArray sortedArrayUsingComparator:^NSComparisonResult(Employee *e1, Employee *e2){\n    return [e1.firstname compare:e2.firstname];    \n}];\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用 NSSortDescriptor 进行排序
\n让\xe2\x80\x99s 假设我们有一个包含自定义类实例的数组,Employee 具有属性名字、姓氏和年龄。下面的示例说明如何创建一个 NSSortDescriptor,该描述符可用于按年龄键对数组内容进行升序排序。

\n\n
NSSortDescriptor *ageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:YES];\nNSArray *sortDescriptors = @[ageDescriptor];\nNSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用自定义比较进行排序
\n名称是字符串,当您对字符串进行排序以呈现给用户时,应始终使用本地化比较。通常您还想执行不区分大小写的比较。这里有一个使用 (localizedStandardCompare:) 的示例,用于按姓氏和名字对数组进行排序。

\n\n
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc]\n              initWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];\nNSSortDescriptor * firstNameDescriptor = [[NSSortDescriptor alloc]\n              initWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];\nNSArray *sortDescriptors = @[lastNameDescriptor, firstNameDescriptor];\nNSArray *sortedArray = [employeesArray sortedArrayUsingDescriptors:sortDescriptors];\n
Run Code Online (Sandbox Code Playgroud)\n\n

有关参考和详细讨论,请参阅:\n https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/SortDescriptors/Articles/Creating.html
\n http://www.ios-blog.co .uk/tutorials/objective-c/how-to-sort-nsarray-with-custom-objects/

\n