iOS小技能:从数组搜索特定条件的元素(利用NSPredicate实现)

1,874 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第34天,点击查看活动详情

前言

//     @distinctUnionOfObjects:返回指定属性去重后的值的数组
//    @unionOfObjects:返回指定属性的值的数组,不去重
    NSMutableArray *accouAr = [self->listArr valueForKeyPath:@"@distinctUnionOfObjects.account"];

  • 利用NSPredicate从数组搜索特定条件的元素
  1. 筛选选中的规格数据
  2. 城市搜索
  3. 多个过滤条件的拼接:银行列表的过滤
  4. 字符串数组的过滤(数组元素为系统的自有类型)
  5. Core Data的数据查询
  6. 使用谓词进行数据分组 (数组元素为自定义类型)
        NSPredicate* predicate = [NSPredicate predicateWithFormat:@"account == %@", key];
        
        NSArray *arFiltered = [  listArr filteredArrayUsingPredicate:predicate];//以一定的条件过滤listArr数组,即进行大数据搜索。

I NSPredicate的应用

  • 谓词(NSPredicate)

是Objective-C中针对数据集合的一种逻辑筛选条件,它类似于数据库中SQL语句对数据筛选的限制约束条件。 Objective-C中的谓词经常用来从数组(Array)、集合(Set)等数据集合中筛选数据元素,谓词约束条件封装在NSPredicate对象中,可通过类函数predicateWithFormat和逻辑约束语句进行初始化

  • 在进行一些数据的筛选的时候经常使用到它:

    CoreData的数据查询、按照特定条件(日期)排序的数据、从数组进行数据查询 例如: 以一定的条件(特定日期)过滤maTemp数组,即进行大数据搜索

  • 与正则表达式结合使用进行数据格式的校验

1.1NSPredicate在正则表达式的应用

kunnan.blog.csdn.net/article/det… NSPredicate在正则表达式的应用:限制UITextField只能输入金额的正则表达式(0. .00)

blog.csdn.net/z929118967/…

1.2 对象数组按照日期分组排序

  1. 将含有日期属性的对象数组按照日期重新分组
  2. 将对象数组按照时间戳排序 kunnan.blog.csdn.net/article/det…

II 从数组搜索特定条件的元素

2.1 筛选选中的规格数据

  • 通过 isSelected 筛选选中的规格数据
- (NSString *)SpecValIds{
    
    
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"isSelected == %d",YES];
    
    
    
    NSMutableArray *tmparr = [self.platProductAttributeAndSpecificationDto.ProductSpecificationDtos filteredArrayUsingPredicate:predicate];
        
    NSArray *editReturnedModeltmparr = [tmparr valueForKeyPath:@"@distinctUnionOfObjects.id"];
    
    NSString *string = [editReturnedModeltmparr componentsJoinedByString:@","];//     iOS 将数组中的元素用符号拼接字符串的方法

    

    return string;
    
}

2.2 城市搜索

blog.csdn.net/z929118967/…

2.3 多个过滤条件的拼接:银行列表的过滤

  • 多个过滤条件的拼接
#pragma mark - ********     //列表的银行过滤
- (void)predicateBankList{
    if ([Session Instance].arrOnlySupportBankList.count == 0) {
        return;
    }
     
    NSMutableString *stringpredicate = [NSMutableString string] ;
//    for (NSString *code in ) {
//        if ([code isEqualToString:[Session Instance].arrOnlySupportBankList[0]]) {
//            [stringpredicate appendFormat:@"  strBankNO ==  %@ ",code];
//        }else {
//            [stringpredicate appendFormat:@"  strBankNO ==  %@ ",code];
//        }
//    }
    NSInteger count = [Session Instance].arrOnlySupportBankList.count;
    for (int i = 0;  i<count;i++) {
         
        if (count == 1) {
            [stringpredicate appendFormat:@"  strBankNO ==  '%@' ",[Session Instance].arrOnlySupportBankList[i]];
        }else if (count>1){
             
            if (i == 0) {
                [stringpredicate appendFormat:@"  strBankNO ==  '%@' ",[Session Instance].arrOnlySupportBankList[i]];
            }else{
                [stringpredicate appendFormat:@"or  strBankNO ==  '%@' ",[Session Instance].arrOnlySupportBankList[i]];
            }
        }
        
    }
    if ([stringpredicate isEqualToString:@"" ] ) {
        return;
    }
     
    NSPredicate *predicate = [NSPredicate predicateWithFormat:stringpredicate];
    self.bankListDepositCard = [NSMutableArray arrayWithArray:[ self.bankListDepositCard  filteredArrayUsingPredicate:predicate]];
    self.bankListCreditCard = [NSMutableArray arrayWithArray:[ self.bankListCreditCard  filteredArrayUsingPredicate:predicate]];
    
}

2.4 字符串数组的过滤(数组元素为系统的自有类型)

  • 字符串数组的过滤
/**
  
 BankInfoData 对象的 的银行卡编号是否包含在 arrOnlySupportBankList 银行卡编号数组中
 @return bool
 */
- (BOOL)isaBankSelectedEqualToBusinessBank:(KNData*)data{
     
  
     
   NSArray *arrOnlySupportBankList=  [KNSession Instance].arrOnlySupportBankList;//kevin
    NSArray *tmp = [NSArray array];
    NSString *stringpredicate = [NSString stringWithFormat:@" self ==  '%@'",data.strBankNO];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:stringpredicate];
    tmp = [NSMutableArray arrayWithArray:[  arrOnlySupportBankList filteredArrayUsingPredicate:predicate]];
    if (tmp.count>0) {
        return YES;
    }else{
        return NO;
    }
}

2.5: Core Data的数据查询


  • 例子:保存未读消息
#pragma mark - 保存未读消息
/** 先查询,在修改 */
+ (void)saveMessageWithMessage:(IPMessageDetailData*)data{
    if (data.messageId == nil || data == nil){
        return;
    }
    
    NSManagedObjectContext *context = [IPMessageDataTool shareContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = [NSEntityDescription entityForName:@"KNMessage" inManagedObjectContext:context];
    // 设置排序(按照messageId降序)
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"messageId" ascending:NO];
    request.sortDescriptors = [NSArray arrayWithObject:sort];
    // 设置条件过滤(搜索name中包含字符串"Itcast-1"的记录,注意:设置条件过滤时,数据库SQL语句中的%要用*来代替,所以%Itcast-1%应该写成*Itcast-1*)
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"messageId = %@  AND operateID = %@",data.messageId,[SessionMgr Instance].operateID];
    request.predicate = predicate;
    // 执行请求
    //    request.fetchLimit = 50;
    NSError *error = nil;
    NSArray *objs = [context executeFetchRequest:request error:&error];
    if (error) {
        [NSException raise:@"查询错误" format:@"%@", [error localizedDescription]];
    }
    Message *tmpMessage;
    if (objs.count>0) {
        tmpMessage = objs[0];
        return;
    }else{
        tmpMessage = [NSEntityDescription insertNewObjectForEntityForName:@"Message" inManagedObjectContext:context];
    }
    tmpMessage.messageExpiredTime = data.messageExpiredTime;
    tmpMessage.messageIssueTime = data.messageIssueTime;
    tmpMessage.messageId = data.messageId;
    tmpMessage.messageTitle = data.messageTitle;
    tmpMessage.operateID = [SessionMgr Instance].operateID;
    tmpMessage.isRead = NO;
    BOOL success = [context save:&error];
    if (!success) {
        [NSException raise:@"访问数据库错误" format:@"%@", [error localizedDescription]];
    }
}

2.6 使用谓词进行数据分组 (数组元素为自定义类型)

  1. 过滤 Style 查找数据
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"Style == %@", [[NSNumber numberWithInteger:type] description]];
    NSArray *arFiltered = [  model.List filteredArrayUsingPredicate:predicate];//以一定的条件(特定日期)过滤maTemp数组,即进行大数据搜索。
    
    model.List = [NSMutableArray arrayWithArray:arFiltered];
    
    return model;
  1. 过滤type 查找电子签名的数据

查找电子签名的数据【避免遍历数组 certificateInfoList,判断type=8】

- (CRMcertificateInfoListDto *)getSignimgURLCRMcertificateInfoListDto{
    

    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"type == %@", [[NSNumber numberWithInteger:CRMcertificateInfoListDtotype4Sign] description]];
    
    
    

    NSArray *arFiltered = [  self.certificateInfoList filteredArrayUsingPredicate:predicate];//以一定的条件(特定日期)过滤maTemp数组,即进行大数据搜索。
    CRMcertificateInfoListDto *dto = nil;
    
    
    if(arFiltered.count>0){
        
        dto =  arFiltered.firstObject;
        

    }
    
    
    return dto;

}

2.7 将 NSArray 转换为 NSDictionary 进行判断是否包含指定元素,将时间复杂度降低到 O(1) 级别

  • 利用+ (NSValue *)valueWithPointer:(nullable const void *)pointer; 将NSArray进行转换NSDictionary
  • 利用@property (nullable, readonly) void *pointerValue;将NSDictionary转为NSArray

字典的 是数组存储的元素

保证后续通过 objectForKey: 判断是否存在指定的 元素

字典的是数组的索引值

保证字典可以恢复为数组

// 将数组转为字典
+ (NSDictionary<NSNumber *, id> *)arr2Dic:(NSArray *)arr {
    NSMutableDictionary *mutableDic = [NSMutableDictionary dictionary];
    [arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        mutableDic[[NSValue valueWithPointer:(__bridge const void * _Nullable)(obj)]] = @(idx);// 数组可能存在相同的元素,将 `NSValue` 切换到自定义类型即可

    }];
    return  [mutableDic copy];
}

// 将字典转为数组
+ (NSArray*)dic2Arr:(NSDictionary<NSValue *, NSNumber *> *)dic {
    NSInteger length = dic.count;
    NSMutableArray *mutableArr = [NSMutableArray arrayWithCapacity:100];
    // 先占位
    for (int i=0; i<length; i++) {
        [mutableArr addObject:[NSNull null]];
    }
    [dic enumerateKeysAndObjectsUsingBlock:^(NSValue * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
        NSInteger idx = obj.intValue;
        mutableArr[idx] = (__bridge id _Nonnull)(key.pointerValue);
    }];
    return [mutableArr copy];
}

  • 测试
    NSLog(@"测试:(没有冲突的情况下,只需hash 一次)");
    // targetObj 在数组中间
    NSObject *targetObj = [ViewController new];

    NSDictionary *dic = [self arr2Dic:arr];
    if ([dic objectForKey:[NSValue valueWithPointer:(__bridge const void * _Nullable)(targetObj)]]) {
        NSLog(@"key 存在");
    }


III 、限制UITextField只能输入金额的正则表达式(0. .00)

在UITextField的代理里面利用正则表达式实现输入限制

限制UITextField只能输入金额

/**
 限制UITextField只能输入金额的正则表达式
 @param textField <#textField description#>
 @param range <#range description#>
 @param string <#string description#>
 @return <#return value description#>
 */
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

    
        NSInteger len = range.length > 0?([textField.text length] - range.length): ([textField.text length] + [string length]);
    
        if(len > 20){
            return false;
        }
    ////
    NSString * str = [NSString stringWithFormat:@"%@%@",textField.text,string];

    return [QCT_Common isAmoutshouldChangeCharactersInRange:str];
    
    
    
    
}

  • 正则表达式
+ (BOOL)isAmoutshouldChangeCharactersInRange:(NSString*)str{
    //匹配以0开头的数字
    NSPredicate * predicate0 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^[0][0-9]+$"];
    //匹配两位小数、整数
    NSPredicate * predicate1 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^(([1-9]{1}[0-9]*|[0])\.?[0-9]{0,2})$"];
    return ![predicate0 evaluateWithObject:str] && [predicate1 evaluateWithObject:str] ? YES : NO;
    
}

see also

iOS数据搜索技巧: kunnan.blog.csdn.net/article/det…

  1. 应用NSPredicate进行数据筛选:从数组搜索特定条件的元素(从数组中筛选type=8的电子签名数据,避免遍历数组 certificateInfoList)
  2. 正则表达式