Approaches to create a nested tree structure of NSDictionaries?

Posted by d11wtq on Stack Overflow See other posts from Stack Overflow or by d11wtq
Published on 2010-05-22T03:35:13Z Indexed on 2010/05/22 3:40 UTC
Read the original article Hit count: 445

Filed under:
|
|

I'm parsing some input which produces a tree structure containing NSDictionary instances on the branches and NSString instance at the nodes.

After parsing, the whole structure should be immutable. I feel like I'm jumping through hoops to create the structure and then make sure it's immutable when it's returned from my method.

We can probably all relate to the input I'm parsing, since it's a query string from a URL. In a string like this:

a=foo&b=bar&a=zip

We expect a structure like this:

NSDictionary {
  "a" => NSDictionary {
    0 => "foo",
    1 => "zip"
  },
  "b" => "bar"
}

I'm keeping it just two-dimensional in this example for brevity, though in the real-world we sometimes see var[key1][key2]=value&var[key1][key3]=value2 type structures. The code hasn't evolved that far just yet.

Currently I do this:

- (NSDictionary *)parseQuery:(NSString *)queryString {
  NSMutableDictionary *params = [NSMutableDictionary dictionary];
  NSArray *pairs = [queryString componentsSeparatedByString:@"&"];

  for (NSString *pair in pairs) {
    NSRange eqRange = [pair rangeOfString:@"="];

    NSString *key;
    id value;

    // If the parameter is a key without a specified value
    if (eqRange.location == NSNotFound) {
      key = [pair stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
      value = @"";
    } else {
      // Else determine both key and value
      key = [[pair substringToIndex:eqRange.location] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
      if ([pair length] > eqRange.location + 1) {
        value = [[pair substringFromIndex:eqRange.location + 1] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
      } else {
        value = @"";
      }
    }

    // Parameter already exists, it must be a dictionary
    if (nil != [params objectForKey:key]) {
      id existingValue = [params objectForKey:key];
      if (![existingValue isKindOfClass:[NSDictionary class]]) {
        value = [NSDictionary dictionaryWithObjectsAndKeys:existingValue, [NSNumber numberWithInt:0], value, [NSNumber numberWithInt:1], nil];
      } else {
        // FIXME: There must be a more elegant way to build a nested dictionary where the end result is immutable?
        NSMutableDictionary *newValue = [NSMutableDictionary dictionaryWithDictionary:existingValue];
        [newValue setObject:value forKey:[NSNumber numberWithInt:[newValue count]]];
        value = [NSDictionary dictionaryWithDictionary:newValue];
      }

    }

    [params setObject:value forKey:key];
  }

  return [NSDictionary dictionaryWithDictionary:params];
}

If you look at the bit where I've added FIXME it feels awfully clumsy, pulling out the existing dictionary, creating an immutable version of it, adding the new value, then creating an immutable dictionary from that to set back in place. Expensive and unnecessary?

I'm not sure if there are any Cocoa-specific design patterns I can follow here?

© Stack Overflow or respective owner

Related posts about objective-c

Related posts about dictionary