iOS开发·必会的算法操作:字符串数组排序+模型对象数组排序

前方的讲话

为给字符串数组排序,除了用C/C++的主导措施,iOS开发者重新应学会使用苹果专门为NSArray
排序提供的sortedArrayUsingComparator 方法:

- (NSArray<ObjectType> *)sortedArrayUsingComparator:(NSComparator NS_NOESCAPE)cmptr NS_AVAILABLE(10_6, 4_0);

其中,需要安装一个NSComparator 参数,它是一个block,查看定义如下:

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

其一block体返回的NSComparisonResult 是一个枚举类型,它的定义是:

typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};

题目来了,怎么设置?

  • 以设置这个NSComparator
    参数的block体,你可以当安其block体的当儿,手动返回一个NSComparisonResult
    枚举类型的之一具体值(NSOrderedAscending, NSOrderedSame,
    NSOrderedDescending 三选一):

image.png

  • 如果数组里面是字符串,在设置其block体的当儿,你呢得以用苹果专门为NSString
    提供的字符串比较艺术,获得一个NSComparisonResult
    类型,将其机动回到。

- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale; // locale arg used to be a dictionary pre-Leopard. We now accept NSLocale. Assumes the current locale if non-nil and non-NSLocale. nil continues to mean canonical compare, which doesn't depend on user's locale choice.

image.png

这会儿,就需要了解NSStringCompareOptions
的意思。但要您找一下NSStringCompareOptions
,会意识众多稿子中之翻译或者中文说以误导,或者特别麻烦看清什么意思?例如下面就篇博客:

image.png

接下来,相同的解说文案还坐勒索传讹的不胫而走来了,例如你看下是博客:

image.png

遂,笔者决定写这个本文,好好展示他们的用处。

1. 率先栽:数组的字符串元素中凡是核心数据列


1.1 字符串数组排序示例

1.1.1 实验代码
  • main.m

void handleSortingForIntStrArray(void){
    NSArray *originalArray = @[@"00",@"0",@"00",@"01",@"10",@"21",@"12",@"11",@"22"];
    //block比较方法,数组中可以是NSInteger,NSString(需要转换)
    NSComparator finderSort = ^(id string1,id string2){
        if ([string1 integerValue] > [string2 integerValue]) {
            return (NSComparisonResult)NSOrderedDescending;
        }else if ([string1 integerValue] < [string2 integerValue]){
            return (NSComparisonResult)NSOrderedAscending;
        }else{
            return (NSComparisonResult)NSOrderedSame;
        }
    };
    //数组排序:
    NSArray *resultArray = [originalArray sortedArrayUsingComparator:finderSort];
    NSLog(@"第一种排序结果:%@",resultArray);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Results of handleSortingForIntArray()**********************");
        handleSortingForIntStrArray();
    }
    return 0;
}
1.1.2 运行结果

image.png

1.1.3 实验结论
  • 据悉数组元素的数值大小返回升序数组

1.2 NSComparator与NSComparisonResult

点的代码中之所以到了NSComparator与NSComparisonResult,在本文的“前面的言语”中既介绍了,这里又排列一下概念。

1.2.1 NSComparator
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
1.2.2 NSComparisonResult
typedef NS_ENUM(NSInteger, NSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending};

2. 次之栽:数组的字符串元素中不是骨干数据类


2.1 示例:字符串数组排序

2.1.1 实验代码
  • main.m

//
//  main.m
//  SortingForArray
//
//  Created by ChenMan on 2017/12/20.
//  Copyright © 2017年 ChenMan. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <stdio.h>

void handleSortingForStrArray(void){
       NSArray *stringsArray = [NSArray arrayWithObjects:
                             @"string b",
                             @"string A",
                             @"string a",
                             @"string \uFF41",
                             @"string a",
                             @"string A",
                             @"string c",
                             @"string d0030",
                             @"string d2",
                             @"アいろはアイウエイウエ",
                             @"アいろはアイウエイウエ",
                             @"アいろはアイウエイウエ",nil];

    NSLocale *currentLocale = [NSLocale currentLocale];
    NSComparator finderSortBlock = ^(id string1,id string2) {

        NSRange string1Range =NSMakeRange(0, [string1 length]);
        return [string1 compare:string2 options:nil range:string1Range locale:currentLocale];
    };

    NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
    NSLog(@"finderSortArray: %@", finderSortArray);

}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Results of handleSortingForStrArray()**********************");
        handleSortingForStrArray();
    }
    return 0;
}
2.1.2 运行结果:

image.png

2.1.3 实验结论:

设齐尝试代码中,有如此一行代码:

return [string1 compare:string2 options:nil range:string1Range locale:currentLocale];

根据运行结果,可领略如下结论:

  • 即使在- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale;中将(NSStringCompareOptions)枚举类型的参数设置为nil,也足以运作。但貌似不这么做,这里只是为着考察不点名该枚举参数上系统的默认设置,并跟本文接下指定该枚举参数的排序结果相比。
  • 可窥见:
    • 默认同一字符的全角字符看做半角字符。不区分同一个字符(如日文的片假字)的半角与全角状态。相同元素,维持原序。
    • 默认区分字母大小写,同一个字符小写以前,大写于后。
    • 字母并非按照unicode码的轻重缓急升序排列。例如,全角a的unicode为FF41,半角a的unicode为0061,半角A的unicode为0041,半角b的unicode为0062,但排序结果是
      全角a = 半角a < 半角A < 半角b
    • 默认不识别含有数字字符的数值大小,0030虽说数学意义比较2要命,但是,仅于字符串的角度看,第一单字符0比2微,所以d0030排在d2前面。
2.1.4 知识展开:

半角与全角字符

  • 全角占少数独字节,半角占一个字节。通常咱们相遇的英文字母、数字键、符号键这种ASCII码系统里头的字符大多数情形下是半角的。

  • 境内汉字输入法输入的汉字为全角,字母数字也半角,但是标点则默认为全角,可切换为半角(可以由此输入法工具条上的呼应按钮来切换标点符号的全角半角状态)。

  • 日文中的发字,也来片假字。这个片假字有星星点点效编码,同一个片假字分别产生半角和全角两栽编码。例如:看起像相同的片假字组成的语句,全角状态字符开头的吧アいろはアイウエイウエ,半角状态字符开头的也アいろはアイウエイウエ。可以看出,明显和一个片假字的全角状态
    半角状态 “胖”一圈。

  • 英文字母其实也时有发生全角字母,例如小写的a,其半角形式的unicode码为0061,其全角形式的unicode码为FF41。可查看Unicode®字符百科官网。

2.2 NSStringCompareOptions

NSStringCompareOptions是一个枚举类型,并非一个类似。打开NSStringCompareOptions的概念,可查看如下

typedef NS_OPTIONS(NSUInteger, NSStringCompareOptions) {
    NSCaseInsensitiveSearch = 1,
    NSLiteralSearch = 2,        /* Exact character-by-character equivalence */
    NSBackwardsSearch = 4,      /* Search from end of source string */
    NSAnchoredSearch = 8,       /* Search is limited to start (or end, if NSBackwardsSearch) of source string */
    NSNumericSearch = 64,       /* Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find */
    NSDiacriticInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 128, /* If specified, ignores diacritics (o-umlaut == o) */
    NSWidthInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 256, /* If specified, ignores width differences ('a' == UFF41) */
    NSForcedOrderingSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 512, /* If specified, comparisons are forced to return either NSOrderedAscending or NSOrderedDescending if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with NSCaseInsensitiveSearch specified) */
    NSRegularExpressionSearch API_AVAILABLE(macos(10.7), ios(3.2), watchos(2.0), tvos(9.0)) = 1024    /* Applies to rangeOfString:..., stringByReplacingOccurrencesOfString:..., and replaceOccurrencesOfString:... methods only; the search string is treated as an ICU-compatible regular expression; if set, no other options can apply except NSCaseInsensitiveSearch and NSAnchoredSearch */
};
2.2.1 NSNumericSearch

合法说明:Added in 10.2; Numbers within strings are compared using
numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only
applies to compare methods, not find

  • 一旦,将齐例被的片代码修改为

void handleSortingForStrArray(void){
    NSArray *stringsArray = [NSArray arrayWithObjects:
                             @"string b",
                             @"string A",
                             @"string a",
                             @"string \uFF41",
                             @"string a",
                             @"string A",
                             @"string c",
                             @"string d0030",
                             @"string d2",
                             @"アいろはアイウエイウエ",
                             @"アいろはアイウエイウエ",
                             @"アいろはアイウエイウエ",nil];
    NSStringCompareOptions comparisonOptions = NSNumericSearch;
    NSLocale *currentLocale = [NSLocale currentLocale];
    NSComparator finderSortBlock = ^(id string1,id string2) {

        NSRange string1Range =NSMakeRange(0, [string1 length]);
        return [string1 compare:string2 options:comparisonOptions range:string1Range locale:currentLocale];
    };

    NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];
    NSLog(@"finderSortArray: %@", finderSortArray);
}
  • 运作结果

image.png

  • 结论
    NSStringCompareOptions指定为NSNumericSearch,当字符串中寓数字时,从数值大小的角度按升序排序。
2.2.2 NSCaseInsensitiveSearch

合法解释:无。英文字面解释:不分字母大小写。

  • 要,将上例被的有代码修改为

NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch; 
  • 运转结果

image.png

  • 结论
    NSStringCompareOptions指定为NSCaseInsensitiveSearch,不区分同一个字母之轻重写状态,如aA作相同元素,若其他条件为同则维持原序。
2.2.3 NSLiteralSearch

官说明:Exact character-by-character equivalence

  • 假使,将达到例被的一些代码修改也

NSStringCompareOptions comparisonOptions = NSLiteralSearch;
  • 运转结果

image.png

  • 结论
    • 区分
      同一个字符(如日文的片假字)的半角与全角状态,同一片假字的全角状态小于半角状态。
    • 另外规则,继续按照系统默认排序规则排序,包括默认区分
      字母大小写,以及其它默认排序规则。
    • 随官方英文说明,这个规则是负分别每个字符的平等状态。只要unicode不同的字符,就无承认他们“等模拟”,即使他们之语言及之含义相同。

  • 题外话
    • 之所以,有的文献说NSLiteralSearch
      是分轻重缓急写是误导,系统以就默认区分
      字母大小写,这些人觉得苹果商店供者力量来画蛇添足干嘛?而且可看官方英文说明,也非是其一意思。只有指定不区分
      字母大小写的NSCaseInsensitiveSearch,要么不写,即默认区分
2.2.4 NSWidthInsensitiveSearch

官说明:If specified, ignores width differences (‘a’ == UFF41)

  • 设若,将上例被之片段代码修改也

NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch;
  • 运作结果

image.png

  • 结论
    • 不区分
      同一个字符(如日文的片假字)的半角与全角状态,同一片假字的全角状态相当半角状态。
    • 另规则,继续遵循系统默认排序规则排序,包括默认区分
      字母大小写,以及任何默认排序规则。
    • 又指定两个时,NSWidthInsensitiveSearch
      NSLiteralSearch 的先级赛,综合起来的结果是不区分
      半角全角。
    • 官方英文说明中的UFF41是指全角a'a'
      是指半角a,如果指定NSWidthInsensitiveSearch,则非区分字符的全角半角,即使你而指定了NSLiteralSearch

就,当有如下代码

NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch | NSLiteralSearch;

其二意图相当给尚未NSLiteralSearch的代码

NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch;
2.2.5 NSForcedOrderingSearch

合法说明:If specified, comparisons are forced to return either
NSOrderedAscending or NSOrderedDescending if the strings are
equivalent but not strictly equal, for stability when sorting (e.g.
“aaa” > “AAA” with NSCaseInsensitiveSearch specified)

  • 假使,将齐例被的一部分代码修改为

NSStringCompareOptions comparisonOptions = NSForcedOrderingSearch;
  • 运作结果

image.png

  • 结论
    • 匪存字符等不同效相不对等的定义了,只要unicode不一致的字符,必须区分,必须返回一个哪个死谁小的结果(NSOrderedAscending
      or NSOrderedDescending)。
    • 自从英文说明为堪看来,NSForcedOrderingSearch
      的优先级最高,即如您以指定了另外有或作用冲突之枚举类型,也以NSForcedOrderingSearch
      的图也仍。
2.2.6 综合采取
  • 一个于多之使示范是,区分字母大小写,区分数值大小,区分半角全角,并强制性指定区分unicode不平等的字符。综合这些规则,写起就是:

NSStringCompareOptions comparisonOptions = NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;
  • 运作结果

image.png

2.2.7 误导用法
  • 自看了起成百上千其它博客用了如此的误导示例:

NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch|NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;

立马其间,NSCaseInsensitiveSearch是为着不区分好小写字母,但是后面更加个NSForcedOrderingSearch想强制区分字符又是怎么回事?虽然,这样描写并无会见报错,运行效果及方的综合示范一摸一样。但这么误导之想法是个逻辑矛盾。不信仰,你瞧它运行的结果:

image.png

3. 数组里面凡是相仿的对象


需要:假设我们根据后台返回的JSON字典数组用MJExtension转换成为模型数组,现在咱们要依据ID或者Age对范数组进行排序。

  • Pesson.m

#import <Foundation/Foundation.h>  

@interface Person : NSObject  
@property (nonatomic,copy) NSString *ID;  
@property (nonatomic,copy) NSString *name;  
@property (nonatomic,assign) int age;  
@end  
  • 据悉int类型的性质对范数组进行排序

NSArray *sortArrayByAgeInt = [self.dataArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {  

    Person *pModel1 = obj1;  
    Person *pModel2 = obj2;  

    if (pModel1.age > pModel2.age) { 
        return NSOrderedDescending;//降序  
    }else if (pModel1.name < pModel2.name){  
        return NSOrderedAscending;//升序  
    }else {  
        return NSOrderedSame;//相等  
    }  

}];
  • 因str类型的性能对范数组进行排序

NSArray *sortArrayByIDStr = [self.dataArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {  

    Person *pModel1 = obj1;  
    Person *pModel2 = obj2;  

    if ([pModel1.ID intValue]> [pModel2.ID intValue]) { 
        return NSOrderedDescending;//降序  
    }else if (pModel1.name < pModel2.name){  
        return NSOrderedAscending;//升序  
    }else {  
        return NSOrderedSame;//相等  
    }  

}];

4. 花样玩法:例题


于OC的高档用法被,经常索要查阅系统类或者某自定义类中之个体属性和民用成员变量,并由此KVC的主意强制修改这些个人成员变量的价,以替代系统或者由定义类中之默认设置。所以,如果你懒得创建有借出数据的屡屡组,可以想到以运行时之主意取成员变量的勤组,并展开排序操作训练。

题1. 请取出NSString看似的满贯公有 属性
并存放到一个屡组,并动用NSArraysortedArrayUsingComparator的办法吃这个数组进行升序排序操作。要求:排序过程中要区分字符全角半角状态,其它可按系统默认条件。

  • 参照代码:
    main.m

void handlePrintingOfProperties(void){
    unsigned int count;// 记录属性个数
    objc_property_t *properties = class_copyPropertyList([NSString class], &count);
    // 生成一个属性名称组成的数组
    NSMutableArray *propertyNameArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        // An opaque type that represents an Objective-C declared property.
        // objc_property_t 属性类型
        objc_property_t property = properties[i];
        // 获取属性的名称 C语言字符串
        const char *cName = property_getName(property);
        // 转换为Objective C 字符串
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        [propertyNameArray addObject:name];
    }
    NSLog(@"排序前的属性列表 = %@",propertyNameArray);

    NSComparator cmptr = ^(NSString *obj1, NSString *obj2){
        return [obj1 compare:obj2 options:NSLiteralSearch];
    };
    NSArray *afterSort = [propertyNameArray sortedArrayUsingComparator:cmptr];
    NSLog(@"排序后的属性列表 = %@",afterSort);

    //C语言中,用完copy,create的东西之后,最好释放
    free(properties);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"handlePrintingOfProperties()**********************");
        handlePrintingOfProperties();
    }
    return 0;
}
  • 运作结果

image.png

题2. 请取出NSURL看似中概括私有 在内的全部
分子变量,并存放到一个屡组,并动用NSArraysortedArrayUsingComparator的道被这数组进行升序排序操作。要求:排序过程中待区分字符全角半角状态,其它可依系统默认条件。

  • 参照代码:

void handlePrintingOfIvars(void){
    unsigned int count;// 记录属性个数
    Ivar *properties = class_copyIvarList([NSURL class], &count);
    // 生成一个属性名称组成的数组
    NSMutableArray *propertyNameArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        // An opaque type that represents an Objective-C declared property.
        // objc_property_t 属性类型
        Ivar property = properties[i];
        // 获取属性的名称 C语言字符串
        const char *cName = ivar_getName(property);
        // 转换为Objective C 字符串
        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        [propertyNameArray addObject:name];
    }
    NSLog(@"排序前的成员变量列表 = %@",propertyNameArray);

    NSComparator cmptr = ^(NSString *obj1, NSString *obj2){
        return [obj1 compare:obj2 options:NSLiteralSearch];
    };
    NSArray *afterSort = [propertyNameArray sortedArrayUsingComparator:cmptr];
    NSLog(@"排序后的成员变量列表 = %@",afterSort);

    //C语言中,用完copy,create的东西之后,最好释放
    free(properties);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"handlePrintingOfIvars()**********************");
        handlePrintingOfIvars();
    }
    return 0;
}
  • 运作结果

image.png

5. 附录:本实验被创造工程说明


其余能够以计算机达推行之种类名为程序,其中,有图形化用户界面的次名为应用
,没有图形界面的次序可以是医护进程
,还有雷同种叫做命令行工具。本文这里关注的凡算法和多少结果,不体贴图形界面,所以新建一个命令行工具即可。创建方法:新建一个macOS工程,选择Command
Line Tool类型,点击下一致步配置工程信息即可。

创造一个命令行工具

工程创造成功