保存到核心数据时,使用谓词来防止从数组中重复输入

时间:2014-11-10 12:41:35

标签: ios objective-c core-data ios7 nspredicate

我有一个书店,为简单起见,它有两个属性。

  1. 书名bookTitle和模型
  2. 中的媒体资源名称为: NSManagedObject
  3. 图书作者bookAuthor和模型中的媒体资源名称为: NSManagedObject
  4. 我有一本书可能只有在没有重复的情况下才需要添加。该数组称为possibleBooksToSaveToArray

    这是我效率低下的方法:

    1. 首先运行获取请求以获取给定实体名称bookEntry
    2. 的所有书籍条目
    3. 然后我运行一个嵌套的for循环来检查bookTitlebookAuthor的每个实例。
    4. 如果两个实例都匹配,那么就会有重复,因此请删除当前对象,以便以后在执行和insert请求时不会保存它。
    5. 如果找到重复项,则中断for循环,然后继续查找其他重复项
    6. 以下是代码:

      -(BOOL)saveUniqueBookEntriesWithArray:(NSArray*)array withCompletionBlock:(void(^)(NSError *error))completionBlock{
      
          NSMutableArray *possibleBooksToSaveToArray = [array mutableCopy];
      
          NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
          NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([BookEntry class]) inManagedObjectContext:self.managedObjectContext];
          [fetchRequest setEntity:entity];
          NSError *error = nil;
          NSArray *result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
      
      
          NSMutableArray *booksAlreadyInDatabaseNoNeedToSaveArray = [[NSMutableArray alloc] init];
          if([result count] > 0){
              for(int i = 0; i < [possibleBooksToSaveToArray count]; i++){
                  for(int k = 0; k < [result count]; k++){
                      RSSBookEntryModel * currentPossibleBookEntry = possibleBooksToSaveToArray[i];
                      BookEntry * currentDBObject = result[k];
                      NSString *cbemTitle = [currentPossibleBookEntry.bookTitle lowercaseString];
                      NSString *cbemAuthor = [currentPossibleBookEntry.bookAuthor lowercaseString];
      
                      NSString *dbTitle = [currentDBObject.bookTitle lowercaseString];
                      NSString *dbAuthor = [currentDBObject.bookAuthor lowercaseString];
      
      
                      if([cbemTitle isEqualToString:dbTitle] && [cbemAuthor isEqualToString:dbAuthor]){
                          [booksAlreadyInDatabaseNoNeedToSaveArray addObject:currentPossibleBookEntry];
                          break;
                      }
                  }
              }
          }
              [possibleBooksToSaveToArray removeObjectsInArray:booksAlreadyInDatabaseNoNeedToSaveArray];
      }    
      //Now run Insert into database code for updated narrowed down array called possibleBooksToSaveToArray
      

      这很好但我知道这可以用谓词来完成。我已经将谓词用于单个属性,但我不知道如何通过数组实现这一点。

      有人可以帮助我吗?

      谢谢

2 个答案:

答案 0 :(得分:1)

只需使用IN运算符即可使用数组执行搜索。因此,查看数组possibleBooksToSaveToArray并假设它是一个对象数组,我们可以使用关键路径“bookTitle”轻松提取标题,我们可以这样做:

NSArray *bookTitlesToAdd = [possibleBooksToSaveToArray valueForKey:@"bookTitle"];
//now we have an array of titles we can fetch against.
//If we want only the books that are missing:

NSFetchRequest *existingBooksFetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([BookEntry class])];
existingBooksFetchRequest.predicate = [NSPredicate predicateWithFormat:@"bookTitle in %@",bookTitlesToAdd];

NSDictionary *existingBooksKeyedByTitle;
NSArray *existingBooks = [context executeFetchRequest:existingBooksFetchRequest error:nil];
if (existingBooks.count > 0) {
    existingBooksKeyedByTitle = [NSDictionary dictionaryWithObjects:existingBooks forKeys:[existingBooks valueForKey:@"bookTitle"]];
}

此时我们现在有一本BookEntry托管对象的字典,这些对象由他们的图书标题键入。这使得迭代书籍更容易添加。

for (NSDictionary *bookToAdd in possibleBooksToSaveToArray) {
    BookEntry *existingBookEntry = existingBooksKeyedByTitle[bookToAdd[@"bookTitle"]];
    if (!existingBookEntry) {
        //we need to add a new book
    } else {
        //we update an existing book
    }
}

如果您想执行大小写和变音符号不敏感搜索,那么您可以做的是将谓词更改为:

[NSPredicate predicateWithFormat:@"bookTitle in [cd]%@",bookTitlesToAdd]

修改

如果您想使用作者和书籍标题进行检查,那么我建议您保留每本书的ISBN号或某种形式的规范标识。即使在SQL中,我也不相信它可以创建一个相同匹配两列的谓词。

[NSPredicate predicateWithFormat:@"bookTitle in %@ && bookAuthor in %@",bookTitlesToAdd,bookAuthorsToAdd]之类的东西会起作用,但是当你期望作者B时,你仍然会得到作者A写的书X,因为作者A在你提供的作者数组中。

所以我绝对推荐使用ISBN方法,否则您也可以将谓词添加到谓词中,并在迭代数组时简单地验证每本书。我认为有多个作者同名书籍的机会非常小,所以你不必过于担心。

答案 1 :(得分:0)

我已经重构了你的代码,以展示我是如何做出类似的事情的。

对每个条目执行获取请求。如果我正确阅读文档,您的ManagedObjectModel已经在内存中,因此不会对性能产生影响。你的内存使用量实际上应该下降。

-(void)saveUniqueBookEntriesWithArray:(NSArray*) possibleBooksToSaveToArray withCompletionBlock:(void(^)(NSError *error))completionBlock {
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([BookEntry class])];
    fetchRequest.entity:entity;

    NSError *error = nil;
    NSArray result;

    for (RSSBookEntryModel *bookEntry in possibleBooksToSaveToArray)
    {
        fetchRequest.predicate = [NSpredicate predicateWithFormat:@"BookTitle = %@ && BookAuthor = %@", bookEntry.bookTitle, bookEntry.bookAuthor];
        result = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

        if (result.count < 1)
        {
            // Save new book to the database
        } else 
        {
            // Book already exists in the database.
        }
    }    
}

希望有所帮助。