How do I sort an NSMutableArray with custom objects in it?

前端 未结 27 3050
予麋鹿
予麋鹿 2020-11-21 04:45

What I want to do seems pretty simple, but I can\'t find any answers on the web. I have an NSMutableArray of objects, and let\'s say they are \'Person\' objects

相关标签:
27条回答
  • 2020-11-21 04:59

    Compare method

    Either you implement a compare-method for your object:

    - (NSComparisonResult)compare:(Person *)otherObject {
        return [self.birthDate compare:otherObject.birthDate];
    }
    
    NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selector(compare:)];
    

    NSSortDescriptor (better)

    or usually even better:

    NSSortDescriptor *sortDescriptor;
    sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate"
                                               ascending:YES];
    NSArray *sortedArray = [drinkDetails sortedArrayUsingDescriptors:@[sortDescriptor]];
    

    You can easily sort by multiple keys by adding more than one to the array. Using custom comparator-methods is possible as well. Have a look at the documentation.

    Blocks (shiny!)

    There's also the possibility of sorting with a block since Mac OS X 10.6 and 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];
    }];
    

    Performance

    The -compare: and block-based methods will be quite a bit faster, in general, than using NSSortDescriptor as the latter relies on KVC. The primary advantage of the NSSortDescriptor method is that it provides a way to define your sort order using data, rather than code, which makes it easy to e.g. set things up so users can sort an NSTableView by clicking on the header row.

    0 讨论(0)
  • 2020-11-21 05:00

    Starting in iOS 4 you can also use blocks for sorting.

    For this particular example I'm assuming that the objects in your array have a 'position' method, which returns an 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];
    

    Note: the "sorted" array will be autoreleased.

    0 讨论(0)
  • 2020-11-21 05:00

    I tried all, but this worked for me. In a class I have another class named "crimeScene", and want to sort by a property of "crimeScene".

    This works like a charm:

    NSSortDescriptor *sorter = [[NSSortDescriptor alloc] initWithKey:@"crimeScene.distance" ascending:YES];
    [self.arrAnnotations sortUsingDescriptors:[NSArray arrayWithObject:sorter]];
    
    0 讨论(0)
  • 2020-11-21 05:02

    Swift's protocols and functional programming makes that very easy you just have to make your class conform to the Comparable protocol, implement the methods required by the protocol and then use the sorted(by: ) high order function to create a sorted array without need to use mutable arrays by the way.

    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)
    
    0 讨论(0)
  • 2020-11-21 05:03

    I did this in iOS 4 using a block. Had to cast the elements of my array from id to my class type. In this case it was a class called Score with a property called points.

    Also you need to decide what to do if the elements of your array are not the right type, for this example I just returned NSOrderedSame, however in my code I though an exception.

    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: This is sorting in descending order.

    0 讨论(0)
  • 2020-11-21 05:03

    For NSMutableArray, use the sortUsingSelector method. It sorts it-place, without creating a new instance.

    0 讨论(0)
提交回复
热议问题