Sasi M Sasi M - 4 months ago 17
Objective-C Question

Sort NSArray custom objects by another NSArray custom objects

I have 2 different NSArray with custom objects as follows,

Item *item1 = [[Items alloc] init];
item1.number = @"1";
item1.serailNumber = @"S01";

Item *item2 = [[Items alloc] init];
item2.number = @"2";
item2.serailNumber = @"S02";

Item *item3 = [[Items alloc] init];
item3.number = @"3";
item3.serailNumber = @"S03";

Item *item4 = [[Items alloc] init];
item4.number = @"4";
item4.serailNumber = @"S04";

Item *item5 = [[Items alloc] init];
item5.number = @"5";
item5.serailNumber = @"S05";

NSArray *items = @[item1, item2, item3, item4, item5];

NSArray *specList = @[@{"number" : @"002", @"serialNumber" : @"S02"},
@{"number" : @"004", @"serialNumber" : @"S04"},
@{"number" : @"003", @"serialNumber" : @"S03"}];


Now I want to sort my
items
array based on
specList
array by comparing "
number
" property.

Now my expected items list is,

@[item2, item4, item3, item1, item5]


I have gone through several samples as listed below but I couldn't figure out how to compare with custom objects. Any help would be appreciated, Thanks in advance.

Sample 1
Sample 2

Answer

This should do the trick:

NSArray *sorted = [items sortedArrayUsingComparator:^NSComparisonResult(Item *item1, Item *item2) {
    NSInteger indexForItemEquivalent1InSpecList = [self indexForItem:item1 inList:specList];
    NSInteger indexForItemEquivalent2InSpecList = [self indexForItem:item2 inList:specList];
    return [@(indexForItemEquivalent1InSpecList) compare:@(indexForItemEquivalent2InSpecList)];
}];

NSLog(@"Sorted: %@", sorted);

With:

-(NSInteger)indexForItem:(Item *)item inList:(NSArray *)list
{
    for (NSInteger i = 0; i < [list count]; i++)
    {
        if ([list[i][@"number"] integerValue] == [[item number] integerValue])
        {
            return i;
        }
    }
    return NSIntegerMax; //If not found, we put it at the end of the list
}

Output:

Sorted: (
    "<Item 0x146678f0> number: 2 serial: S02",
    "<Item 0x14667e10> number: 4 serial: S04",
    "<Item 0x14667900> number: 3 serial: S03",
    "<Item 0x14654200> number: 1 serial: S01",
    "<Item 0x14667e20> number: 5 serial: S05"
)

I override -description to make log clearer:

-(NSString *)description
{
    return [NSString stringWithFormat:@"<%@ %p> number: %@ serial: %@", [self class], self, _number, _serailNumber];
}

In other words:
You have to find the index of the corresponding Item object inside specList (see indexForItem:inList:). I used integerValue because you are using @"002" and @"2", which aren't equal strings.
Then in the NSComparator you compare the two indexes.

For item1 and item5 which are at the end, I let them as if. There is no guarantee of their order since they are not present in specList. If you want to put them in "ascending" order, you have to do instead:

NSInteger indexForItem1InSpecList = [self indexForItem:item1 inList:specList];
NSInteger indexForItem2InSpecList = [self indexForItem:item2 inList:specList];
if (indexForItem1InSpecList == NSIntegerMax && indexForItem2InSpecList == NSIntegerMax)
{
    return [@([[item1 number] integerValue]) compare:@([[item2 number] integerValue])];
}
else
{
    return [@(indexForItem1InSpecList) compare:@(indexForItem2InSpecList)];
}