【问题标题】:Compare two arrays of dictionaries and add/remove items比较两个字典数组并添加/删除项目
【发布时间】:2015-06-04 09:10:41
【问题描述】:

我有两个这样的字典数组

  arrCurrent = [{
        id = 1;
        name = "Name1";
    },{
        id = 2;
        name = "Name2";
    },{
        id = 3;
        name = "Name3";
    }];


    arrUpdated = [{
        id = 1;
        name = "Name1 has changed";
    },{
        id = 2;
        name = "Name2 has changed";
    },{
        id = 4;
        name = "Name4 is a new item";
    }];

我的要求是根据以下条件合并这两个数组

1) 如果 arrUpdated 包含新项目(具有新 id 的项目),则应将其添加为新项目。

2) 如果 arrUpdated 包含与 arrCurrent 包含的 id 相同的项目,则应将这些项目替换为更新后的项目

所以,最终的数组应该是这样的

arrFinal = [{
        id = 1;
        name = "Name1 has changed";
    },{
        id = 2;
        name = "Name2 has changed";
    },{
        id = 3;
        name = "Name3";
    },{
        id = 4;
        name = "Name4 is a new item";
    }];

希望我的要求很明确,最好的方法是什么?

这就是我正在尝试的方式。但是这种方法会得到重复的项目。另外,遍历数组也不是最好的方法

请注意:实际代码包含与上述数组名称不同的名称。但同样的逻辑

arrSavedSectors = arrCurrent

arrAllUpdatedSectors = arrUpdated

arrFilteredSectors = arrFinal

NSMutableArray *arrSavedSectors = [[NSMutableArray alloc]initWithArray:  [dictionary objectForKey:@"ArrSectors"]];
NSMutableArray *arrFilteredSectors = [[NSMutableArray alloc]initWithArray:arrSavedSectors];

// add updated sectors to list
NSArray *arrAllUpdatedSectors = [NSArray arrayWithArray:[sectorDetails objectForKey:@"AllUpdatedSectors"]];

if([arrSavedSectors count] > 0){
    // check for updated sectors (Which are already saved in the plist, but recently updated some details)
    for(NSDictionary *dicSector in arrSavedSectors){
        for(NSDictionary *dicUpdatedSector in arrAllUpdatedSectors){
            if([[dicUpdatedSector objectForKey:@"id"] isEqualToString:[dicSector objectForKey:@"id"]]){
                [arrFilteredSectors removeObject:dicSector];
                [arrFilteredSectors addObject:dicUpdatedSector];
            }
            else{
                [arrFilteredSectors addObject:dicUpdatedSector];
            }
        }

    }
}
else{
    [arrFilteredSectors addObjectsFromArray:arrAllUpdatedSectors];
}

【问题讨论】:

  • 显示你目前拥有的代码。
  • 参考 vikingosegundo 答案。这是可以接受的答案。

标签: ios objective-c nsarray nsdictionary


【解决方案1】:

一些 Set 操作的时间。

最难的部分是我们需要让我们使用的集合理解对象是相等和相同的。

字典都不是,但我们可以引入一个包装类来使用它的实现。

@interface Wrapper : NSObject
@property NSDictionary *dictionary;
-(instancetype)initWithDictionary: (NSDictionary *)dict;
@end

@implementation Wrapper

-(instancetype)initWithDictionary: (NSDictionary *)dict
{
    self = [super init];
    if (self) {
        self.dictionary = dict;
    }
    return self;
}

-(BOOL)isEqual:(Wrapper *)object
{
    return [self.dictionary[@"id"] isEqual: object.dictionary[@"id"]];
}

-(NSUInteger)hash
{
    return [self.dictionary[@"id"] unsignedIntegerValue];
}

-(NSString *)description
{
    return [self.dictionary description];
}

@end

现在我们可以用它来包装每个字典

NSArray *arrCurrent = @[[[Wrapper alloc] initWithDictionary: @{@"id": @(1), @"name" : @"Name 1"}],
                        [[Wrapper alloc] initWithDictionary: @{@"id": @(2), @"name" : @"Name 2"}],
                        [[Wrapper alloc] initWithDictionary: @{@"id": @(3), @"name" : @"Name 3"}]];

NSArray *arrUpdated = @[[[Wrapper alloc] initWithDictionary: @{@"id": @(1), @"name" : @"Name 1 has Changed"}],
                        [[Wrapper alloc] initWithDictionary: @{@"id": @(2), @"name" : @"Name 2 has Changed"}],
                        [[Wrapper alloc] initWithDictionary: @{@"id": @(4), @"name" : @"Name 4"}]];

接下来我们从这个创建集合

NSSet *setCurrent = [NSSet setWithArray:arrCurrent];
NSSet *setUpdated = [NSSet setWithArray:arrUpdated];

现在我们做集合算术

NSMutableSet *setFinal = [setUpdated mutableCopy];
[setFinal unionSet:setCurrent];

最后我们使用键值编码来解开字典

NSArray *arrFinal = [setFinal valueForKey:@"dictionary"];

结果:

{(
    {
        id = 1;
        name = "Name 1 has Changed";
    },
        {
        id = 2;
        name = "Name 2 has Changed";
    },
        {
        id = 3;
        name = "Name 3";
    },
        {
        id = 4;
        name = "Name 4";
    }
)}

此代码的一个问题:包装器在其他情况下没有用,因为确定相等性的规则可能不同,因此能够在需要时定义它们会很有用。我们可以用块来做到这一点:

包装器变成

@interface Wrapper : NSObject
@property id object;
@property (copy) BOOL (^equalComparator)(id a, id b);
@property (copy) NSUInteger (^hashBlock)(id a);
@end

@implementation Wrapper

-(instancetype)initWithObject: (id)obj
              equalComparator:(BOOL (^)(id a, id b))equalComparator
                    hashBlock:(NSUInteger (^)(id a))hashBlock
{
    self = [super init];
    if (self) {
        self.equalComparator = equalComparator;
        self.hashBlock = hashBlock;
        self.object = obj;
    }
    return self;
}

-(BOOL)isEqual:(Wrapper *)object
{
    return self.equalComparator(self.object, object.object);
}

-(NSUInteger)hash
{
    return self.hashBlock(self.object);
}

-(NSString *)description
{
    return [self.object description];
}

@end

我们像这样使用它

BOOL (^eq)(NSDictionary *a, NSDictionary *b) = ^(NSDictionary *a, NSDictionary *b){
    return [a[@"id"] isEqual: b[@"id"]];
};

NSUInteger (^hash)(NSDictionary *a) = ^(NSDictionary *a){
    return [a[@"id"] unsignedIntegerValue];
};

NSArray *arrCurrent = @[[[Wrapper alloc] initWithObject: @{@"id": @(1), @"name" : @"Name 1"} equalComparator:eq hashBlock:hash],
                        [[Wrapper alloc] initWithObject: @{@"id": @(2), @"name" : @"Name 2"} equalComparator:eq hashBlock:hash],
                        [[Wrapper alloc] initWithObject: @{@"id": @(3), @"name" : @"Name 3"} equalComparator:eq hashBlock:hash]];

NSArray *arrUpdated = @[[[Wrapper alloc] initWithObject: @{@"id": @(1), @"name" : @"Name 1 has Changed"} equalComparator:eq hashBlock:hash],
                        [[Wrapper alloc] initWithObject: @{@"id": @(2), @"name" : @"Name 2 has Changed"} equalComparator:eq hashBlock:hash],
                        [[Wrapper alloc] initWithObject: @{@"id": @(4), @"name" : @"Name 4"} equalComparator:eq hashBlock:hash]];

NSSet *setCurrent = [NSSet setWithArray:arrCurrent];
NSSet *setUpdated = [NSSet setWithArray:arrUpdated];
NSMutableSet *setFinal = [setUpdated mutableCopy];
[setFinal unionSet:setCurrent];

NSArray *arrFinal = [setFinal valueForKey:@"object"];

【讨论】:

    【解决方案2】:

    我认为循环会做到这一点。

       for(int x=0;x<[arrUpdate count];x++){
           //condition 2
            if([[arrCurrent valueForKey:@"id"] containsObject:[[arrUpdate objectAtIndex:x] valueForKey:@"id"]]){
    
           }
         //condition 1
          else{
             [arrCurrent addObject: [arrUpdate objectAtIndex:x]]
    
           }
        }
    

    虽然你也可以使用谓词。

    我没有测试此代码,因为我没有机器。但你至少可以修改它。

    更新:

    我最终找到了一台机器来证明我的代码有效。使用提问者提供的测试数据:

    NSMutableArray *arrCurrent = [NSMutableArray arrayWithArray:@[@{
        @"id":@"1", @"name":@"Name1"
    },@{
        @"id":@"2", @"name":@"Name2"
    },@{
        @"id":@"3", @"name":@"Name3"
    }]];
    
    
    NSMutableArray *arrUpdated = [NSMutableArray arrayWithArray:@[@{
        @"id":@"1", @"name":@"Name1 has changed"
    },@{
        @"id":@"2", @"name":@"Name2 has changed"
    },@{
        @"id":@"4", @"name":@"Name4 is a new item"
    }]];
    
    
    for(int x=0;x<[arrUpdated count];x++){
        //condition 2
        if([[arrCurrent valueForKey:@"id"] containsObject:[[arrUpdated objectAtIndex:x] valueForKey:@"id"]]){
    
            //start updating current array
            for(int y=0;y<[arrCurrent count];y++){
                if([[[arrCurrent objectAtIndex:y] valueForKey:@"id"] isEqualToString:[[arrUpdated objectAtIndex:x] valueForKey:@"id"]]){
                    NSLog(@" (%@) will change to (%@)",[[arrCurrent objectAtIndex:y] valueForKey:@"name"],[[arrUpdated objectAtIndex:x] valueForKey:@"name"]);
                    [arrCurrent replaceObjectAtIndex:y withObject:[arrUpdated objectAtIndex:x] ];
                    break;
                }
            }
    
    
        }
        //condition 1
        else{
            [arrCurrent addObject: [arrUpdated objectAtIndex:x]];
    
        }
    }
    NSLog(@"RESULT>: %@",arrCurrent);
    

    结果:>>

    2015-06-04 23:24:30.949 dd[7523:60b]  (Name1) will change to (Name1 has changed)
        2015-06-04 23:24:30.950 dd[7523:60b]  (Name2) will change to (Name2 has changed)
        2015-06-04 23:24:30.951 dd[7523:60b] RESULT>: (
                {
                id = 1;
                name = "Name1 has changed";
            },
                {
                id = 2;
                name = "Name2 has changed";
            },
                {
                id = 3;
                name = Name3;
            },
                {
                id = 4;
                name = "Name4 is a new item";
            }
        )
    

    【讨论】:

    • 这个编码没有给出答案。请不要打勾,否则会给出减号。这部分的结果是 - ( { id = 1; name = "Name1 has changed"; }, { id = 2; name = "Name2 已更改"; }, { id = 4; name = "Name4 是一个新项目"; } )
    • 这就是为什么我说我没有测试它并让提问者修改它。也许他修改了它并能够得到正确的答案。
    • 我已经更新了我的答案并使用了我以前的代码。如您所见,它运行良好。
    • 因为我证明你错了而拒绝我过去的答案? @user3182143
    • 人们可能更喜欢我的代码而不是你的代码的一个原因:你的代码过于复杂。即,您使用 if 和 else 语句嵌套 for 循环。这意味着对于forifelsebreak 的每次调用,代码中的不同执行路径都会演变——增加了复杂性并随之增加了出错空间。另一方面,我的代码仅使用高级抽象。 (事实上​​,唯一的 if 语句是在包装器 init 中找到的,并且比必需品更传统)。如果你看我的最后一个代码块:无论你如何改变输入数组,下面的代码执行路径都保持不变。
    猜你喜欢
    • 2013-02-09
    • 1970-01-01
    • 2013-02-02
    • 2020-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-31
    • 1970-01-01
    相关资源
    最近更新 更多