如何从NSDictionary中删除空值

19

我有一个 JSON 数据源:

{
    "count1" = 2;
    "count2" = 2;
    idval = 40;
    level = "<null>";
    "logo_url" = "/assets/logos/default_logo_medium.png";
    name = "Golf Club";
    "role_in_club" = Admin;
}

问题是"<null>"。在将其保存到NSUserDefaults之前,我无法弄清如何从NSDictionary中删除它。


因为你可以将空值保存到NSUserDefaults中。 - jimbob
啊,"在保存到NSUserDefaults之前" - 抱歉,我没看到那里。 - user529758
https://dev59.com/z3HYa4cB1Zd3GeqPNJnb#40343081 - Abdurrahman Mubeen Ali
14个回答

50

另一种变体,不需要(显式)循环:

NSMutableDictionary *dict = [yourDictionary mutableCopy];
NSArray *keysForNullValues = [dict allKeysForObject:[NSNull null]];
[dict removeObjectsForKeys:keysForNullValues];

你可以通过将 [NSNull null] 替换为 @"" 来删除所有的空条目。 - Toland Hon
1
这是一段很酷的代码,但如果我们有字典转换成数组,我们可以做些什么呢? - Hemant Singh Rathore

13

遍历字典并查找任何空条目并将其移除。

NSMutableDictionary *prunedDictionary = [NSMutableDictionary dictionary];
for (NSString * key in [yourDictionary allKeys])
{
    if (![[yourDictionary objectForKey:key] isKindOfClass:[NSNull class]])
        [prunedDictionary setObject:[yourDictionary objectForKey:key] forKey:key];
}

之后,prunedDictionary 应该拥有原始字典中所有非空项目。


我在想哪种方法的性能更好:是创建一个可变字典的副本并从中删除对象,还是像这样手动将所有值添加到字典中。 - Richard J. Ross III
1
除非数组非常大,否则它并不重要。如果数组足够大以至于它很重要,我会尝试两种方式,并选择哪种更快。 - Simon Goldeen

4
这里是一个基于类别的递归解决方案,用于包含字典和数组的字典,其值也可以是字典和数组:

文件NSDictionary+Dario.m:

#import "NSArray+Dario.h"

@implementation NSDictionary (Dario)

- (NSDictionary *) dictionaryByReplacingNullsWithEmptyStrings {

    const NSMutableDictionary *replaced = [NSMutableDictionary new];
    const id nul = [NSNull null];
    const NSString *blank = @"";

    for(NSString *key in self) {
        const id object = [self objectForKey:key];
        if(object == nul) {
            [replaced setObject:blank forKey:key];
        } else if ([object isKindOfClass:[NSDictionary class]]) {
            [replaced setObject:[object dictionaryByReplacingNullsWithEmptyStrings] forKey:key];
        } else if ([object isKindOfClass:[NSArray class]]) {
            [replaced setObject:[object arrayByReplacingNullsWithEmptyStrings] forKey:key];
        } else {
            [replaced setObject:object forKey:key];
        }
    }
    return [NSDictionary dictionaryWithDictionary:(NSDictionary*)replaced];
}

@end

文件 NSArray+Dario.m:

#import "NSDictionary+Dario.h"

@implementation NSArray (Dario)

- (NSArray *) arrayByReplacingNullsWithEmptyStrings {
    const NSMutableArray *replaced = [NSMutableArray new];
    const id nul = [NSNull null];
    const NSString *blank = @"";

    for (int i=0; i<[self count]; i++) {
        const id object = [self objectAtIndex:i];

        if ([object isKindOfClass:[NSDictionary class]]) {
            [replaced setObject:[object dictionaryByReplacingNullsWithEmptyStrings] atIndexedSubscript:i];
        } else if ([object isKindOfClass:[NSArray class]]) {
            [replaced setObject:[object arrayByReplacingNullsWithEmptyStrings] atIndexedSubscript:i];
        } else if (object == nul){
            [replaced setObject:blank atIndexedSubscript:i];
        } else {
            [replaced setObject:object atIndexedSubscript:i];
        }
    }
    return [NSArray arrayWithArray:(NSArray*)replaced];
}

3

使用此代码从字典中删除空值

- (NSMutableDictionary *)recursive:(NSMutableDictionary *)dictionary {
for (NSString *key in [dictionary allKeys]) {    
    id nullString = [dictionary objectForKey:key];   
    if ([nullString isKindOfClass:[NSDictionary class]]) {
        [self recursive:(NSMutableDictionary*)nullString];
    } else {
        if ((NSString*)nullString == (id)[NSNull null])
            [dictionary setValue:@"" forKey:key];
    }
}
return dictionary;
}

这会将 null 替换为 @"",这可能是你想要的,也可能不是 :-) - Ben Clayton
[dictionary setValue:@"" forKey:key]; 这可能会导致崩溃。子/递归字典可能不是可变字典。因此,我们可以使用一个新的可变字典来收集每个非空和子字典值。 - rotoava

0

我相信这是最节省资源的方法

// 在NSDictionary上实现类别

- (NSDictionary *)dictionaryByRemovingNullValues {

    NSMutableDictionary * d;
    for (NSString * key in self) {

        if (self[key] == [NSNull null]) {
            if (d == nil) {
                d = [NSMutableDictionary dictionaryWithDictionary:self];
            }
            [d removeObjectForKey:key];
        }

    }

    if (d == nil) {
        return self;
    }

    return d;
}

0
我已经为NSJSON序列化类创建了一个分类。
创建一个分类并导入类以使用其方法...
// Mutable containers are required to remove nulls.
if (replacingNulls)
{
    // Force add NSJSONReadingMutableContainers since the null removal depends on it.
    opt = opt || NSJSONReadingMutableContainers;
}

id JSONObject = [self JSONObjectWithData:data options:opt error:error];

if ((error && *error) || !replacingNulls)
{
    return JSONObject;
}

[JSONObject recursivelyReplaceNullsIgnoringArrays:ignoreArrays withString:replaceString];
return JSONObject;

0
在您的视图控制器中添加这3个方法,并像以下方式调用此方法。
NSDictionary *dictSLoginData = [self removeNull:[result valueForKey:@"data"]];

- (NSDictionary*)removeNull:(NSDictionary *)dict {

    NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary: dict];

    const id nul = [NSNull null];
    const NSString *blank = @"";

    for (NSString *key in [dict allKeys]) {

        const id object = [dict objectForKey: key];
        if (object == nul) {
            [replaced setObject: blank forKey: key];
        } else if([object isKindOfClass: [NSDictionary class]]) {
            [replaced setObject: [self replaceNull:object] forKey: key];
        } else if([object isKindOfClass: [NSArray class]]) {
            [replaced setObject: [self replaceNullArray:object] forKey: key];
        }
    }
    return [NSDictionary dictionaryWithDictionary: replaced];
}

- (NSArray *)replaceNullArray:(NSArray *)array {

    const id nul = [NSNull null];
    const NSString *blank = @"";

    NSMutableArray *replaced = [NSMutableArray arrayWithArray:array];

    for (int i=0; i < [array count]; i++) {
        const id object = [array objectAtIndex:i];
        if (object == nul) {
            [replaced replaceObjectAtIndex:i withObject:blank];
        } else if([object isKindOfClass: [NSDictionary class]]) {
            [replaced replaceObjectAtIndex:i withObject:[self replaceNull:object]];
        } else if([object isKindOfClass: [NSArray class]]) {
            [replaced replaceObjectAtIndex:i withObject:[self replaceNullArray:object]];
        }
    }
    return replaced;
}

- (NSDictionary *)replaceNull:(NSDictionary *)dict {

    const id nul = [NSNull null];
    const NSString *blank = @"";

    NSMutableDictionary *replaced = [NSMutableDictionary dictionaryWithDictionary: dict];

    for (NSString *key in [dict allKeys]) {
        const id object = [dict objectForKey: key];
        if (object == nul) {
            [replaced setObject: blank forKey: key];
        } else if ([object isKindOfClass: [NSDictionary class]]) {
            [replaced setObject: [self replaceNull:object] forKey: key];
        } else if([object isKindOfClass: [NSArray class]]) {
            [replaced setObject: [self replaceNullArray:object] forKey: key];
        }
    }
    return replaced;
}

0

我是这样做的。

NSMutableDictionary *prunedDict = [NSMutableDictionary dictionary];
[self enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
  if (![obj isKindOfClass:[NSNull class]]) {
    prunedDict[key] = obj;
  }
}];

0
以下代码在结果为数组或字典时是可行的,您可以通过编辑代码将返回的结果更改为 nil 或空字符串。
该函数是递归的,因此可以解析数组和字典。
-(id)changeNull:(id)sender{
    
    id newObj;
    
    if ([sender isKindOfClass:[NSArray class]]){

        NSMutableArray *newArray = [[NSMutableArray alloc] init];
        
        
        for (id item in sender){
        
            [newArray addObject:[self changeNull:item]];
        
        }
        
        newObj = newArray;
    }
    
    else if ([sender isKindOfClass:[NSDictionary class]]){
        
        NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
        
        for (NSString *key in sender){
            
            NSDictionary *oldDict = (NSDictionary*)sender;
            
            
            id item = oldDict[key];

            if (![item isKindOfClass:[NSDictionary class]] && ![item isKindOfClass:[NSArray class]]){

                if ([item isEqual:[NSNull null]]){
                    item = @"";
                    
                }
                
                [newDict setValue:item forKey:key];

            }
            else{
                
                [newDict setValue:[self changeNull:item] forKey:key];
                
            }
        }
        newObj = newDict;
    }
    
    return newObj;
}

造成的结果是:
jsonresult (
    {
        Description = "<null>";
        Id = 1;
        Name = High;
    },
        {
        Description = "<null>";
        Id = 2;
        Name = Medium;
    },
        {
        Description = "<null>";
        Id = 3;
        Name = Low;
    }
)

change null (
    {
        Description = "";
        Id = 1;
        Name = High;
    },
        {
        Description = "";
        Id = 2;
        Name = Medium;
    },
        {
        Description = "";
        Id = 3;
        Name = Low;
    }
)

0

要删除它,将其转换为可变字典,并删除键为“level”的对象;

NSDictionary* dict = ....;  // this is the dictionary to modify
NSMutableDictionary* mutableDict = [dict mutableCopy];
[mutableDict removeObjectForKey:@"level"];
dict = [mutableDict copy];

如果您不使用ARC,您需要添加一些调用 'release'。
更新:
如果您不知道带有"<null>"对象的键的名称,则必须进行迭代:
NSDictionary* dict = ....;  // this is the dictionary to modify
NSMutableDictionary* mutableDict = [dict mutableCopy];
for (id key in dict) {
    id value = [dict objectForKey: key];
    if ([@"<null>" isEqual: value]) {
        [mutableDict removeObjectForKey:key];
    }
}
dict = [mutableDict copy];

为了定位"<null>"值,我使用字符串比较,因为在您的示例中"<null>"是一个字符串。但我不确定这是否真的是这种情况。


这并不是那样的情况。如果它是一个字符串,那就不会有问题。这里的问题是解析将产生一个读取“<null>”的NSNull实例,而该值无法存储在 NSUserDefaults 中。 - Gabriele Petronella
我有点困惑。问题中的示例不是JSON,而很可能是字典的NSLog输出(在解析JSON数据之后)。如果键level的值是NSNull实例,则应该打印<null>而不是"<null>"。因此,要么该值是字符串,要么该示例是错误的。 - Codo
Gabriella,你是正确的。问题在于它获取了一个存储为“<null>”的值,而无法识别它。通常它会给我一个类似于 null 的值。 - jimbob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接