سؤال

I've got a layered NSMutableDictionary object and i'd like to be able to remove dictionaries deeper down in the hierarchy. Is there a quick and easy way to do this, for example, a removeObjectAtKeyPath-like method? Can't seem to find one.

Thanks!

هل كانت مفيدة؟

المحلول

Nothing built in, but your basic category method will do just fine:

@implementation NSMutableDictionary (WSSNestedMutableDictionaries)

- (void)WSSRemoveObjectForKeyPath: (NSString *)keyPath
{
    // Separate the key path
    NSArray * keyPathElements = [keyPath componentsSeparatedByString:@"."];
    // Drop the last element and rejoin the path
    NSUInteger numElements = [keyPathElements count];
    NSString * keyPathHead = [[keyPathElements subarrayWithRange:(NSRange){0, numElements - 1}] componentsJoinedByString:@"."];
    // Get the mutable dictionary represented by the path minus that last element
    NSMutableDictionary * tailContainer = [self valueForKeyPath:keyPathHead];
    // Remove the object represented by the last element
    [tailContainer removeObjectForKey:[keyPathElements lastObject]];
}

@end

N.B. That this requires that the second-to-last element of the path -- the tailContainer be something that responds to removeObjectForKey:, probably another NSMutableDictionary. If it's not, boom!

نصائح أخرى

You can create a category :

This is upto 1 level down:

#import "NSMutableDictionary+RemoveAtKeyPath.h"

@implementation NSMutableDictionary (RemoveAtKeyPath)

-(void)removeObjectAtKeyPath:(NSString *)keyPath{

    NSArray *paths=[keyPath componentsSeparatedByString:@"."];

    [[self objectForKey:paths[0]] removeObjectForKey:paths[1]];

}

@end

It is called as :

NSMutableDictionary *adict=[[NSMutableDictionary alloc]initWithDictionary:@{@"key1" : @"obj1", @"key11":@"obj11"}];

NSMutableDictionary *bdict=[[NSMutableDictionary alloc]initWithDictionary:@{@"key2" : adict}];

NSLog(@"%@",bdict);
NSLog(@"%@",[bdict valueForKeyPath:@"key2.key1"]);

[bdict removeObjectAtKeyPath:@"key2.key1"];
NSLog(@"After category : %@",bdict);

Minor improvement to Josh's answer, in order to handle keypaths which don't contain a period (i.e. keypaths which are actually keys):

- (void)removeObjectAtKeyPath:(NSString *)keyPath
{
    NSArray *keyPathElements = [keyPath componentsSeparatedByString:@"."];
    NSUInteger numElements = [keyPathElements count];
    if (numElements == 1) {
        [self removeObjectForKey:keyPath];
    } else {
        NSString *keyPathHead = [[keyPathElements subarrayWithRange:(NSRange){0, numElements - 1}] componentsJoinedByString:@"."];
        NSMutableDictionary *tailContainer = [self valueForKeyPath:keyPathHead];
        [tailContainer removeObjectForKey:[keyPathElements lastObject]];
    }
}

I know this is an older post, but I needed to find the same solution in Swift 2.0, unable to find a simple answer I came up with this solution:

public extension NSMutableDictionary {
    public func removeObjectAtKeyPath(keyPath:String) {
        let elements = keyPath.componentsSeparatedByString(".")
        let head = elements.first!
        if elements.count > 1 {
            let tail = elements[1...elements.count-1].joinWithSeparator(".")
            if let d = valueForKeyPath(head) as? NSMutableDictionary {
                d.removeObjectAtKeyPath(tail)
            }
        }else{
            removeObjectForKey(keyPath)
        }
    }
}

I've added an extension to NSMutableDictionary using recursion to step thru the keyPath

I just iterate on jscs's accepted answer with minor improvement - working on the KVC key-path using NSString's built-in pathUtlities category.

@implementation NSMutableDictionary (NestedMutableDictionaries)
- (void)removeObjectForKeyPath: (NSString *)keyPath
{
    NSArray *key = [keyPath pathExtension];
    NSString *keyPathHead = [keyPath stringByDeletingPathExtension];
    [[self valueForKeyPath:keyPathHead] removeObjectForKey:key];
}
@end

Just a little nicer, I think.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top