Objective-C:有选择地组合两个数组

Posted

技术标签:

【中文标题】Objective-C:有选择地组合两个数组【英文标题】:Objective-C: Selectively combine two arrays 【发布时间】:2018-02-13 16:17:03 【问题描述】:

我知道 NSArray 连接在 Objective-C 中的正常工作方式。这不是那个问题。

我的数据正在从 Web 服务进行增量更新。我的对象具有以下类定义(删除了很​​多):

//  NoteTemplate.h
#import <UIKit/UIKit.h>
@interface NoteTemplate 

@property (copy, nonatomic) NSString *objectId;

我正在设备上缓存这些列表并在启动时检查以查看我的数据库中是否有任何新的或更新的NoteTemplate 对象要加载。所以,我最终得到了两个数组:

NSArray <NoteTemplate *> *oldArray
NSArray <NoteTemplate *> *newArray

如果没有更新,那么我只需将两个数组连接在一起即可。

如果有更新,但是,我想合并这两个数组,但只要有共同的objectIdnewArray 中的项目应该优先于@987654328 中的项目@。

到目前为止,我是这样暴力破解的:

- (void)updateNoteTemplatesWithArray:(NSArray *)newTemplates 
    NSArray *oldTemplates = [self getNoteTemplates];
    NSMutableArray *combined = [NSMutableArray arrayWithArray:newTemplates];
    for (NoteTemplate *noteTemplate in oldTemplates) 
        NSArray *matches = [combined filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id blockTemplate, NSDictionary<NSString *,id> *bindings) 
            return [((NoteTemplate *)blockTemplate).objectId isEqualToString:noteTemplate.objectId];
        ]];
        if (matches.count == 0) 
            [combined addObject:noteTemplate];
        
    
    [self setNoteTemplates:[combined copy]];

有没有更优化的方法来做到这一点?我看不出这会影响性能,所以也许不需要优化。不过,这种方法让人感觉很老套,而且设计过度。

【问题讨论】:

你真的关心顺序而不是重复吗?使用带有键objectId 的字典怎么样,然后在需要时对它们进行排序?也许是NSMutableOrderedSet(见***.com/questions/25587420/…) 【参考方案1】:

要使用 Set 扩展 @Larme 的建议,您可以尝试以下方法:

@interface NoteTemplate: NSObject

@property (copy, nonatomic) NSString *objectId;
@property (copy, nonatomic) NSString *text;

- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text;

@end

@implementation NoteTemplate
- (instancetype)initWithObjectId:(NSString *)objectId text:(NSString *)text 
    self = [super init];
    if (self != nil) 
        _objectId = objectId;
        _text = text;
    
    return self;


- (BOOL)isEqual:(id)object 
    return [self.objectId isEqualToString:[object objectId]];


@end

以及使用代码:

NoteTemplate *nt1 = [[NoteTemplate alloc] initWithObjectId:@"1" text:@"old set"];
NoteTemplate *nt2 = [[NoteTemplate alloc] initWithObjectId:@"2" text:@"old set"];
NoteTemplate *nt3 = [[NoteTemplate alloc] initWithObjectId:@"1" text:@"new set"];
NoteTemplate *nt4 = [[NoteTemplate alloc] initWithObjectId:@"3" text:@"new set"];

NSSet <NoteTemplate *> *oldSet = [NSSet setWithObjects:nt1, nt2, nil];
NSSet <NoteTemplate *> *newSet = [NSSet setWithObjects:nt3, nt4, nil];

NSMutableSet <NoteTemplate *> *mergedSet = [newSet mutableCopy];
[mergedSet unionSet:oldSet];

for (NoteTemplate *note in mergedSet) 
    NSLog(@"Set item %@ %@", note.objectId, note.text);

执行此代码后,您将在日志中看到:

Set item 3 new set
Set item 1 new set
Set item 2 old set

我想这就是你要找的东西。

【讨论】:

这是一个不错的方法。我认为顺序在我的回答中很重要,但也许不是。 @TomHarrington 如果顺序很重要,NSOrderSet 可以按照您的建议使用。然后 mergedSet 应该从 oldSet 创建而不是 unionSet 函数应该替换为 [[mergedSet minus:newSet] union:newSet] 哦,对了。我一直在做 Swift,以至于我忘了寻找 NSMutableOrderedSet。相反,我查看了NSOrderedSet,并没有考虑寻找额外的可变性方法。 我最终使用了它的修改版本,因为顺序 确实 对我来说变得很重要,但在我组合数组的部分期间并不重要。我还需要能够删除项目,所以除了isEqual: 方法之外,我还添加了hash 方法。感谢您让我一路走好!【参考方案2】:

我不知道我是否会称其为优雅,但这是一种不那么野蛮的方法。不是每次循环都过滤combined,而是提前获取所有新的ID,并检查循环中的ID列表。

    NSMutableArray <NoteTemplate *> *combined = [NSMutableArray arrayWithArray:newTemplates];
    NSArray <NSString *> *newTemplateIds = [newTemplates valueForKey:@"objectId"];

    for (NoteTemplate *oldTemplate in oldTemplates) 
        if (![newTemplateIds containsObject:oldTemplate.objectId]) 
            [combined addObject:oldTemplate];
        
    

【讨论】:

以上是关于Objective-C:有选择地组合两个数组的主要内容,如果未能解决你的问题,请参考以下文章

可以在 Objective-C 中组合两个用户类吗?

数组排序冒泡排序快速排序选择排序

在 Objective-C 中组合韩语字符

关于objective-c数组,这些方法你得知道

在没有回调的情况下将选择器分配给Objective-C中的C函数

如何使用objective-c从数组中选择不同的图像