Objective-C:有选择地合并两个数组
Objective-C: Selectively combine two arrays
我知道 NSArray
连接在 Objective-C 中的正常工作方式。这不是那个问题。
我有正在从网络服务增量更新的数据。我的对象具有以下 class 定义(删除了很多):
// NoteTemplate.h
#import <UIKit/UIKit.h>
@interface NoteTemplate
@property (copy, nonatomic) NSString *objectId;
我正在设备上缓存这些列表,并在启动时检查我的数据库中是否有任何新的或更新的 NoteTemplate
对象要加载。所以,我最终得到了两个数组:
NSArray <NoteTemplate *> *oldArray
NSArray <NoteTemplate *> *newArray
如果没有更新,那么我需要做的就是简单地将两个数组连接在一起,就是这样。
如果有更新,但是,我想合并两个数组,但是只要有共同的objectId
,newArray
中的项目应优先于 oldArray
.
中的项目
到目前为止,我是这样暴力破解的:
- (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]];
}
有没有更优化的方法来做到这一点?我看不出这会影响性能,所以也许不需要优化。不过,这种方法感觉很老套,而且设计过度。
我不知道我是否会称其为优雅,但这是一种不那么粗鲁的方法。不是每次循环都过滤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];
}
}
要使用 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
我想这就是您要找的。
我知道 NSArray
连接在 Objective-C 中的正常工作方式。这不是那个问题。
我有正在从网络服务增量更新的数据。我的对象具有以下 class 定义(删除了很多):
// NoteTemplate.h
#import <UIKit/UIKit.h>
@interface NoteTemplate
@property (copy, nonatomic) NSString *objectId;
我正在设备上缓存这些列表,并在启动时检查我的数据库中是否有任何新的或更新的 NoteTemplate
对象要加载。所以,我最终得到了两个数组:
NSArray <NoteTemplate *> *oldArray
NSArray <NoteTemplate *> *newArray
如果没有更新,那么我需要做的就是简单地将两个数组连接在一起,就是这样。
如果有更新,但是,我想合并两个数组,但是只要有共同的objectId
,newArray
中的项目应优先于 oldArray
.
到目前为止,我是这样暴力破解的:
- (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]];
}
有没有更优化的方法来做到这一点?我看不出这会影响性能,所以也许不需要优化。不过,这种方法感觉很老套,而且设计过度。
我不知道我是否会称其为优雅,但这是一种不那么粗鲁的方法。不是每次循环都过滤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];
}
}
要使用 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
我想这就是您要找的。