Sort NSArray to a particular sequence using an NSComparisonResult

180 views Asked by At

I'm trying to sort my arrays with a particular sequence using an NSComparisonResult. I'm unable to figure out how to achieve the sequence I'm wanting.

I'm trying to weight Emojis towards the top (sequence of Emojis doesn't matter), followed by A-Z letters with giving a weight towards lowercase before uppercase, followed by numbers, followed by punctuation, then by symbols, and whatever else after that I dont care about at this point. I've gotten pretty close so far, but am still coming up short with what I want.

The sequence I'm trying to achieve would look like this as the output:

("\Ud83d\Ude03",
a,
A,
aa,
aA,
ab,
aB,
a1,
A1,
1,
01,
11,
001,
0001,
1001,
"#",
"#a",
"#1",
"$12",
"$0012")

Based upon this array as the input:

@[ @"a", @"aA", @"aa", @"A", @"aB", @"11", @"1001", @"ab", @"001", @"01", 
   @"a1", @"A1", @"", @"0001", @"1", @"#", @"$12", @"$0012", @"#a", @"#1" ];

But this is the output I'm getting:

("\Ud83d\Ude03",
a,
A,
aA,
aa,
aB,
ab,
a1,
A1,
0001,
001,
01,
1,
1001,
11,
"#a",
"#1",
"$0012",
"$12",
"#")

Code:

- (NSArray *)sortedArray:(NSArray *)input
{
    NSArray *newArray = [input sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2)
    {
        NSString *nameOne = obj1;
        NSString *nameTwo = obj2;

        NSString *startOne;
        NSString *startTwo;

        NSInteger currentIndex = 0;
        NSInteger maxIndex = (nameOne.length < nameTwo.length) ? nameOne.length : nameTwo.length;

        NSCharacterSet *decimalDigitCharSet = [NSCharacterSet decimalDigitCharacterSet];

        NSCharacterSet *punctuationCharSet = [NSCharacterSet punctuationCharacterSet];

        NSCharacterSet *symbolCharSet = [NSCharacterSet symbolCharacterSet];

        NSMutableCharacterSet *nonPriorityCharSet = [[NSMutableCharacterSet alloc]init];

        [nonPriorityCharSet formUnionWithCharacterSet:punctuationCharSet];
        [nonPriorityCharSet formUnionWithCharacterSet:symbolCharSet];

        do
        {
            if (currentIndex < maxIndex)
            {
                startOne = [nameOne substringWithRange:NSMakeRange(currentIndex, 1)];
                startTwo  = [nameTwo substringWithRange:NSMakeRange(currentIndex, 1)];
                currentIndex++;
            }
            else
            {
                if (nameOne.length == nameTwo.length)
                {
                    return NSOrderedSame;
                }
                else
                {
                    return (nameOne.length < nameTwo.length) ? NSOrderedAscending : NSOrderedDescending;
                }
            }
        }
        while ([startOne isEqualToString:startTwo]);
        {
            NSRange rangeOne = [startOne rangeOfCharacterFromSet:nonPriorityCharSet];
            NSRange rangeTwo = [startTwo rangeOfCharacterFromSet:nonPriorityCharSet];

            if (rangeOne.length > 0 || rangeTwo.length > 0)
            {
                return (rangeOne.length > 0) ? NSOrderedDescending : NSOrderedAscending;
            }

            NSRange decimalRangeOne = [startOne rangeOfCharacterFromSet:decimalDigitCharSet];
            NSRange decimalRangeTwo = [startTwo rangeOfCharacterFromSet:decimalDigitCharSet];

            if (decimalRangeOne.length > 0 || decimalRangeTwo.length > 0)
            {
                if (decimalRangeOne.length == decimalRangeTwo.length)
                {
                    return (startOne.intValue > startTwo.intValue) ? NSOrderedDescending : NSOrderedAscending;
                }
                else if (decimalRangeOne.length > decimalRangeTwo.length)
                {
                    return NSOrderedDescending;
                }
                else if (decimalRangeTwo.length > decimalRangeOne.length)
                {
                    return NSOrderedAscending;
                }
            }
        }

        return  [nameOne localizedCaseInsensitiveCompare:nameTwo];
   }];

    return newArray;
}
1

There are 1 answers

3
pteofil On BEST ANSWER

You started well. But you didn't properly check for all the rules that you have set. I have created some categories based on you rules, and sort using them.

- (NSArray *)sortedArray:(NSArray *)input
{
    __block id blocksafeSelf = self;

    NSArray *newArray = [input sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSString *nameOne = obj1;
        NSString *nameTwo = obj2;

        NSInteger currentIndex = 0;
        unichar charOne = [nameOne characterAtIndex:0];
        unichar charTwo = [nameTwo characterAtIndex:0];

        short maxLength = MIN(nameOne.length, nameTwo.length);

        do {
            charOne = [nameOne characterAtIndex:currentIndex];
            charTwo = [nameTwo characterAtIndex:currentIndex];
            currentIndex ++;
        }
        while (charOne == charTwo && currentIndex < maxLength);

        short oneCategory = [blocksafeSelf getCharCategory:charOne];
        short twoCategory = [blocksafeSelf getCharCategory:charTwo];

        if (oneCategory != twoCategory) {
            return oneCategory > twoCategory;
        }
        else if (oneCategory != 1) {
            if (nameOne.length != nameTwo.length) {
                return nameOne.length > nameTwo.length;
            }
            else {
                return charOne > charTwo;
            }
        }
        else {
            if (nameOne.length != nameTwo.length) {
                return nameOne.length > nameTwo.length;
            }
            else {
                oneCategory = [blocksafeSelf getLetterCategory:charOne];
                twoCategory = [blocksafeSelf getLetterCategory:charTwo];

                if (oneCategory == twoCategory) {
                    return charOne > charTwo;
                }
                else {
                    unichar tempCharOne = oneCategory == 7 ? charOne + 32 : charOne;
                    unichar tempCharTwo = twoCategory == 7 ? charTwo + 32 : charTwo;

                    if (tempCharOne != tempCharTwo) {
                        return tempCharOne > tempCharTwo;
                    }
                    else {
                        return oneCategory > twoCategory;
                    }
                }
            }
        }
        return  [nameOne localizedCaseInsensitiveCompare:nameTwo];
    }];

    return newArray;
}

- (short)getCharCategory:(unichar)character {

    if (character > 255) {     // emoji
        return 0;
    }

    NSCharacterSet *letterCaseCharSet = [NSCharacterSet letterCharacterSet];
    if  ([letterCaseCharSet characterIsMember:character]) return 1;

    NSCharacterSet *decimalDigitCharSet = [NSCharacterSet decimalDigitCharacterSet];
    if  ([decimalDigitCharSet characterIsMember:character]) return 2;

    NSCharacterSet *punctuationCharSet = [NSCharacterSet punctuationCharacterSet];
    if  ([punctuationCharSet characterIsMember:character]) return 3;

    NSCharacterSet *symbolCharSet = [NSCharacterSet symbolCharacterSet];
    if  ([symbolCharSet characterIsMember:character]) return 4;

    return 5;

}

- (short)getLetterCategory:(unichar)character {

    NSCharacterSet *lowerCaseCharSet = [NSCharacterSet lowercaseLetterCharacterSet];
    if  ([lowerCaseCharSet characterIsMember:character]) return 6;

    return 7;
}