前面的话

为了给字符串数组排序,除了用 C/C++ 的基本办法,iOS 开发者更应该学会利用苹果专门为 NSArray 排序提供的sortedArrayUsingComparator方法:

- (NSArray *)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(NSIntegerNSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSameNSOrderedDescending};

问题来了,怎么设置?

为了设置这个 NSComparator 参数的 block 体,你可以在设置其 block 体的时候,手动返回一个 NSComparisonResult 枚举类型的某个具体值(NSOrderedAscending, NSOrderedSame, NSOrderedDescending 三选一):

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

如果数组里面是字符串,在设置其 block 体的时候,你也可以利用苹果专门为 NSString 提供的字符串比较方法,获得一个 NSComparisonResult 类型,将其自动返回。

// 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.
- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale; 

数组转换成字符串_php数组转换js数组_字符数组和字符串数组

这时候,就需要了解NSStringCompareOptions的意思。但如果你搜索一下 NSStringCompareOptions,会发现很多文章中的翻译或者中文解释在误导,或者很难看清什么意思?例如下面这篇博客:

字符数组和字符串数组_数组转换成字符串_php数组转换js数组

然后,相同的解释文案还以讹传讹的传开来了,例如你看下面这个博客:

php数组转换js数组_数组转换成字符串_字符数组和字符串数组

于是,笔者决定写此本文,好好展示他们的用途。

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 运行结果

php数组转换js数组_数组转换成字符串_字符数组和字符串数组

1.1.3 实验结论1.2 NSComparator 与 NSComparisonResult

上面的代码中用到了 NSComparator 与 NSComparisonResult,在本文的开头中已经介绍过,这里重新列一下定义。

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

typedef NS_ENUM(NSIntegerNSComparisonResult) {NSOrderedAscending = -1L, NSOrderedSameNSOrderedDescending};

2. 第二种:数组的字符串元素里面不是基本数据类型2.1 示例:字符串数组排序2.1.1 实验代码

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

#import 
#import 

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 运行结果

数组转换成字符串_php数组转换js数组_字符数组和字符串数组

2.1.3 实验结论

如上实验代码中,有这样一行代码:

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

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

1) 即使在- (NSComparisonResult)compare:(NSString *)string options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToCompare locale:(nullable id)locale;中将NSStringCompareOptions枚举类型的参数设置为 nil,也可以运行。但一般不这么做,这里只是为了观察不指定该枚举参数时候系统的默认设置,并与本文接下来指定该枚举参数的排序结果对比。

2) 可以发现:

2.1.4 知识拓展:

半角与全角字符

1) 全角占两个字节,半角占一个字节。通常我们碰到的英文字母、数字键、符号键这种 ASCII 码系统里面的字符大多数情况下是半角的。

2) 国内汉字输入法输入的汉字为全角,字母数字为半角,但是标点则默认为全角,可切换为半角(可以通过输入法工具条上的相应按钮来切换标点符号的全角半角状态)。

3) 日文里面的有汉字,也有片假字。这个片假字有两套编码,同一个片假字分别有半角和全角两种编码。例如:看起来像一样的片假字组成的句子,全角状态ア字符开头的为アいろはアイウエイウエ,半角状态ア字符开头的为アいろはアイウエイウエ。可以看到,明显同一个片假字的全角状态比半角状态 “胖”一圈。

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

2.2 NSStringCompareOptions

NSStringCompareOptions 是一个枚举类型,并非一个类。打开 NSStringCompareOptions 的定义,可查看如下:

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

typedef NS_OPTIONS(NSUIntegerNSStringCompareOptions) {
    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);
}

运行结果如下

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

结论是 NSStringCompareOptions 指定为 NSNumericSearch,当字符串中含有数字时,从数值大小的角度按升序排序。

2.2.2 NSCaseInsensitiveSearch

官方解释:无。英文字面解释:不区分字母大小写。

假设数组转换成字符串,将上例中的部分代码修改为

NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch

运行结果

php数组转换js数组_数组转换成字符串_字符数组和字符串数组

结论是 NSStringCompareOptions 指定为 NSCaseInsensitiveSearch,不区分同一个字母的大小写状态,如 a 与 A 看做相同元素,若其它条件也一致则保持原序。

2.2.3 NSLiteralSearch

官方解释:Exact character-by-character equivalence

假设,将上例中的部分代码修改为

NSStringCompareOptions comparisonOptions = NSLiteralSearch;

运行结果

php数组转换js数组_数组转换成字符串_字符数组和字符串数组

结论

1)区分同一个字符(如日文的片假字)的半角与全角状态,同一片假字的全角状态小于半角状态。

2) 其它规则,继续按系统默认排序规则排序,包括默认区分字母大小写,以及其它默认排序规则。

按照官方英文说明,这个规则是指区分每个字符的等效状态。只要 unicode 不同的字符,就不认可他们“等效”数组转换成字符串,即使他们的语言上的含义相同。

题外话

所以,有的文献说 NSLiteralSearch 是区分大小写是误导,系统本就默认区分字母大小写,这些人以为苹果公司提供这个功能来画蛇添足干嘛?而且可以看看官方英文说明,也不是这个意思。只有指定不区分字母大小写的 NSCaseInsensitiveSearch,要么不写,即默认区分。

2.2.4 NSWidthInsensitiveSearch

官方解释:If specified, ignores width differences (‘a’ == UFF41)

假设,将上例中的部分代码修改为

NSStringCompareOptions comparisonOptions = NSWidthInsensitiveSearch;

运行结果

数组转换成字符串_php数组转换js数组_字符数组和字符串数组

结论

1)不区分同一个字符(如日文的片假字)的半角与全角状态,同一片假字的全角状态等于半角状态。

2) 其它规则,继续按系统默认排序规则排序,包括默认区分字母大小写,以及其它默认排序规则。

3) 同时指定两个时,NSWidthInsensitiveSearch 比 NSLiteralSearch 的优先级高,综合起来的结果是不区分半角全角。

4) 官方英文说明中的 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;

运行结果

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

结论

1) 不存在字符等不等效相不相等的概念了,只要 unicode 不一样的字符,必须区分,必须返回一个谁大谁小的结果(NSOrderedAscending or NSOrderedDescending)。

2) 从英文说明也可以看出,NSForcedOrderingSearch的优先级最高,即如果你同时指定了其它有可能作用冲突的枚举类型,也以 NSForcedOrderingSearch 的作用为准。

2.2.6 综合应用

一个比较多的应用示例是,区分字母大小写,区分数值大小,区分半角全角,并强制性指定区分 unicode 不一样的字符。综合这些条件,写起来就是:

NSStringCompareOptions comparisonOptions = NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;

运行结果

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

2.2.7 误导用法

我看过有很多其它博客用了这样的误导示例:

NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch|NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;

这里面,NSCaseInsensitiveSearch 是为了不区分大小写字母,但是后面再加个 NSForcedOrderingSearch 想强制区分字符又是怎么回事?虽然,这样写并不会报错,运行效果跟上面的综合示例一摸一样。但这样误导的想法是个逻辑矛盾。不信,你看看它运行的结果:

php数组转换js数组_数组转换成字符串_字符数组和字符串数组

3. 数组里面是类的对象

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

// Pesson.m

#import   

@interface Person : NSObject  
@property (nonatomic,copyNSString *ID;  
@property (nonatomic,copyNSString *name;  
@property (nonatomic,assignint 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 类的全部公有属性并存放到一个数组,并利用 NSArray的sortedArrayUsingComparator的方法给这个数组进行升序排序操作。要求:排序过程中需要区分字符全角半角状态,其它可按系统默认条件。

参考代码:

//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;
}

运行结果

数组转换成字符串_php数组转换js数组_字符数组和字符串数组

题2. 请取出 NSURL 类中包括私有在内的全部成员变量,并存放到一个数组,并利用 NSArray 的sortedArrayUsingComparator的方法给这个数组进行升序排序操作。要求:排序过程中需要区分字符全角半角状态,其它可按系统默认条件。

参考代码:

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;
}

运行结果

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

5. 附录:本实验中创建工程说明

任何能在计算机上执行的项目称之为程序,其中,有图形化用户界面的程序称之为应用,没有图形界面的程序可以是守护进程 ,还有一种称之为命令行工具。本文这里关注的是算法和数据结果,不关注图形界面,所以新建一个命令行工具即可。创建方法:新建一个 macOS 工程,选择Command Line Tool类型,点击下一步配置工程信息即可。

字符数组和字符串数组_php数组转换js数组_数组转换成字符串

字符数组和字符串数组_数组转换成字符串_php数组转换js数组

排序算法演示小DEMO

数组转换成字符串_php数组转换js数组_字符数组和字符串数组

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击网站首页每天更新
站 长 微 信: aiwo51889