比较来自两个NSArrays的对象的有效方法

时间:2015-01-21 09:59:14

标签: ios objective-c arrays cocoa-touch

我有两个阵列,一个有1400个记录,一个有450个。较大的数组是具有相应药物ID的药物的“搜索术语”列表。第二个数组包含“药物对象”本身,大约15个字段包含有关药物的各种信息。

我需要的是一个新的'搜索术语对象'数组,它实际上是搜索术语数组,其中所有搜索术语都被药物替换(但搜索术语已添加到对象中)。

到目前为止我所做的是将另一个属性(searchTerm)添加到我的药物对象中。我循环搜索术语数组,然后对于每个搜索词我遍历药物对象数组,当我找到匹配的DrugID时,我创建一个药物对象的新实例,复制原始药物对象的所有信息并填充searchTerm来自搜索词数组。

这看起来效率不高,需要600,000次迭代才能完全填充新阵列,大约需要1分钟。

    FormularyDBManager *formularyDBManagerInstance = [FormularyDBManager new];
    NSArray *searchTermArray = [NSMutableArray arrayWithArray:[formularyDBManagerInstance getSearchDrugs:@"*" from:@"formulary_searchname" orderBy:@"DrugName"]];

    int counter = 0; // this counter is here purely for testing

    for (NSDictionary *searchDrug in searchTermArray) {

        for (Drug *aDrug in arrayOfDrugs) { // this array is populated in a previous method

            counter ++; 
            NSLog(@"Counter = %i", counter);

            if ([searchDrug[@"DrugID"] isEqualToString:aDrug.drugID]) {

                Drug *currentDrug = [Drug new];

                currentDrug.therapeuticGroup2 = aDrug.therapeuticGroup2;
                currentDrug.use = aDrug.use;
                currentDrug.action = aDrug.action;
                currentDrug.therapeuticGroup1 = aDrug.therapeuticGroup1;
                // more properties.... then the search term is added
                currentDrug.drugName = searchDrug[@"searchTerm"];

                [arrayOfSearchDrugs addObject:currentDrug];

            }

        }

    }

    [_tableViewDrugs reloadData];

这样做的正确方法是什么?感谢。

根据Guillaume的回答

解决方案

NSArray *resultArray = [NSMutableArray arrayWithArray:[formularyDBManagerInstance getSearchDrugs:@"*" from:@"formulary_searchname" orderBy:@"DrugName"]];

DataBaseMananger *dataBaseManagerInstance = [DataBaseMananger new];

for (NSDictionary *searchDrug in resultArray) {

    // get the drug object from the new drugs dictionary where the drugID matches the current item in the returned 'search items' array
    Drug *aDrug = [dictOfDrugs objectForKey:searchDrug[@"DrugID"]];  

    Drug *currentDrug = [Drug new];
    NSMutableArray *columns = [NSMutableArray new];
    NSMutableArray *values = [NSMutableArray new];

    currentDrug.therapeuticGroup2 = aDrug.therapeuticGroup2;
    [columns addObject:@"TherapeuticGroup2"];
    [values  addObject:currentDrug.therapeuticGroup2];

    currentDrug.use = aDrug.use;
    [columns addObject:@"Use"];
    [values addObject:currentDrug.use];

    currentDrug.action = aDrug.action;
    [columns addObject:@"Action"];
    [values addObject:currentDrug.action];

    currentDrug.therapeuticGroup1 = aDrug.therapeuticGroup1;
    [columns addObject:@"TherapeuticGroup1"];
    [values addObject:currentDrug.therapeuticGroup1];

    // other properties.....

    currentDrug.searchTerm = searchDrug[@"searchTerm"];
    [columns addObject:@"searchTerm"];
    [values addObject:currentDrug.searchTerm];

    // now add the new 'search term objects' to a database table so they can be retrieved even quicker later on.
    [dataBaseManagerInstance insertToTable:@"formulary_searchDrug" setColumns:columns equals:values];
    [arrayOfSearchDrugs addObject:currentDrug];

}

[_tableViewDrugs reloadData];

更新

将数组初始化(进入我的数据库导入方法)从循环中大量帮助,不确定为什么我真的在那里。

UITableView从相关阵列获取数据的加载时间从55秒开始,在修改代码后减少到21秒,并且在将数组初始化退出循环后将其缩短为10秒。 / p>

6 个答案:

答案 0 :(得分:2)

如果arrayOfDrugs中的所有药物都有唯一的drugID,那么您可以创建一个由其ID标记的药物的地图(字典),并抛弃内部循环。

这应该使复杂性从O(n * m)变为O(n + m)(n的大小为searchTermArraym的大小为arrayOfDrugs) *

这应该类似于:

FormularyDBManager *formularyDBManagerInstance = [FormularyDBManager new];
NSArray *searchTermArray = [NSMutableArray arrayWithArray:[formularyDBManagerInstance getSearchDrugs:@"*" from:@"formulary_searchname" orderBy:@"DrugName"]];

for (NSDictionary *searchDrug in searchTermArray) {

        Drug *aDrug = dictOfDrugs[aDrug.drugID]
        Drug *currentDrug = [Drug new];

        currentDrug.therapeuticGroup2 = aDrug.therapeuticGroup2;
        currentDrug.use = aDrug.use;
        currentDrug.action = aDrug.action;
        currentDrug.therapeuticGroup1 = aDrug.therapeuticGroup1;
        // more properties.... then the search term is added
        currentDrug.drugName = searchDrug[@"searchTerm"];

        [arrayOfSearchDrugs addObject:currentDrug];
    }
}

当然不要忘记更改创建arrayOfDrugs数组的方法以返回字典。

*不要在此基础上接受我的说法,自从我上次尝试在某种程度上正式表达我的代码复杂性以来已经有好几年了。

答案 1 :(得分:0)

如果你必须检查两个数组的每个对象并且期望有多个结果,那么你无能为力。如果你期望一个结果,你可以在找到它时打破循环。但是,如果从循环中删除 NSLog ,它肯定会提高性能和所用时间。

答案 2 :(得分:0)

这里只是一个意识形态,不确定它是否真的会有所改进,但是如果你循环使用较小的数组并使用nspredicate来过滤你需要的drugID的较大的那个呢?

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"propertyName == %@", @"value"];
NSArray *filteredArray = [myArray filteredArrayUsingPredicate:predicate];

答案 3 :(得分:0)

在所有情况下,不同类型的对象的匹配可以减少到同时某个对象的相等性(在您的情况下是成员drugID和关键字“DrugID”的字典值),最快放一个将该项集合作为临时字典,并将该对象作为键。

答案 4 :(得分:0)

您可以使用O(n), n = number of drugs设置算法来获得NS(Mutable)Set

我的示例会将搜索字词映射到字典中包含该搜索字词的药物列表。

她的主要操作是将一组与另一组相交。

让我们开始吧:

你没有为Drug提供课程,所以我创建了自己的课程 由于我不知道药物,我只是使用随机数作为属性。我将通过一个方法来访问它们,这将返回一个集合来执行交集。

@interface Drug : NSObject
@property (nonatomic, strong) NSNumber *f1;
@property (nonatomic, strong) NSNumber *f2;
@property (nonatomic, strong) NSNumber *f3;
@property (nonatomic, strong) NSNumber *f4;
@property (nonatomic, strong) NSNumber *f5;
@property (nonatomic, strong) NSNumber *f6;
@property (nonatomic, strong) NSNumber *f7;
@property (nonatomic, strong) NSNumber *f8;
@property (nonatomic, strong) NSNumber *f9;
@property (nonatomic, strong) NSNumber *f10;
@property (nonatomic, strong) NSNumber *f11;
@property (nonatomic, strong) NSNumber *f12;
@property (nonatomic, strong) NSNumber *f13;
@property (nonatomic, strong) NSNumber *f14;
@property (nonatomic, strong) NSNumber *f15;

@property (nonatomic, strong, readonly) NSSet *drugProperties;
@end


static unsigned int numberOfSearchTerms = 14000;
static unsigned int numberOfDrugs = 450;
@implementation Drug

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.f1 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f2 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f3 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f4 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f5 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f6 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f7 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f8 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f9 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f10 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f11 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f12 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f13 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f14 = @(arc4random_uniform(numberOfSearchTerms) +1);
        self.f15 = @(arc4random_uniform(numberOfSearchTerms) +1);
    }
    return self;
}

-(NSSet *)drugProperties
{
    return [NSSet setWithArray:@[_f1, _f2, _f3, _f4, _f5, _f6, _f7,_f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15]];
}

@end

我的示例中的搜索字词也是数字。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    for (int x = 0; x<5; ++x) {
        NSMutableDictionary *searchTermDrugMapping =[@{} mutableCopy];
        NSMutableSet *searchTerms = [NSMutableSet set];

        for(NSUInteger i = 1; i< numberOfSearchTerms+1; ++i){
            //                [searchTerms addObject:[[SeachrTermSetWrapper alloc] initWithSearchTerm:@(i)]];
            [searchTerms addObject:@(i)];
            searchTermDrugMapping[@(i)] = [@[] mutableCopy];

        }

        NSMutableArray *drugs = [@[] mutableCopy];
        for (NSUInteger i = 0; i< (numberOfDrugs << x) ; ++i) {
            [drugs addObject: [[Drug alloc] init]];
        }

        NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
        for (Drug *d in drugs) {
            NSMutableSet *dp = [d.drugProperties mutableCopy];
            [dp intersectSet:searchTerms];
            [dp enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
                [searchTermDrugMapping[obj] addObject:d];
            }];
        }
        NSTimeInterval stop = [NSDate timeIntervalSinceReferenceDate];
        //NSLog(@"%@", searchTermDrugMapping);

        NSLog(@"%u \t\t%f",(numberOfDrugs << x), stop -start);
    }


    return YES;
}

输出(药物数量,秒数)iOS 8.1 iPhone 5s

450         0.015040
900         0.027976
1800        0.057547
3600        0.117761
7200        0.235752

药物数量增加一倍的时间加倍 - &gt;线性行为 - &gt; O(n)

这很容易。但它会变得更加复杂。要做那样的Set操作,你需要在不同的集合中对象的相等性。 NSHipster has a great article about it。但是我拥有不同种类的物品。这很简单:编写包装这些对象并使它们彼此接受相等的类。将您的真实对象包裹在它们的实例中并将它们放入集合中。

答案 5 :(得分:-4)

您可以使用containsObject方法

例如:

for (NSDictionary *checkData1 in  objects) {

      if (![objects2 containsObject:checkData1]) {

     }
}
像这样....