Skip to content

Instantly share code, notes, and snippets.

@axiixc
Created April 29, 2011 18:06
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save axiixc/948736 to your computer and use it in GitHub Desktop.
Save axiixc/948736 to your computer and use it in GitHub Desktop.
A mutable ordered dictionary collection for Objective-C.
@interface AXMutableOrderedDictionary : NSMutableDictionary <NSMutableCopying> {
@private
NSMutableArray * _keys;
NSMutableDictionary * _dict;
}
+ (id)newWithCapacity:(NSUInteger)initialCapacity;
+ (id)newWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary;
+ (id)newWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
+ (id)orderedDictionary;
+ (id)orderedDictionaryWithCapacity:(NSUInteger)initialCapacity;
+ (id)orderedDictionaryWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary;
+ (id)orderedDictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
- (id)initWithCapacity:(NSUInteger)initialCapacity;
- (id)initWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary;
- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys;
- (NSDictionary *)dictionaryRepresentation;
- (NSArray *)arrayRepresentation;
- (NSArray *)keys;
- (id)keyForObject:(id)object;
- (id)keyForObjectAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfObject:(id)object;
- (NSUInteger)indexOfObjectWithKey:(id)aKey;
- (id)objectForKey:(id)aKey;
- (id)objectAtIndex:(NSUInteger)index;
- (id)lastObject;
- (void)addObject:(id)object withKey:(id)aKey;
- (void)addObjects:(NSArray *)objects withKeys:(NSArray *)keys;
- (void)insertObject:(id)object withKey:(id)aKey atIndex:(NSUInteger)index;
- (void)insertObject:(id)object withKey:(id)aKey afterObject:(id)anotherObject;
- (void)insertObject:(id)object withKey:(id)aKey beforeObject:(id)anotherObject;
- (void)insertObject:(id)object withKey:(id)aKey afterObjectWithKey:(id)anotherKey;
- (void)insertObject:(id)object withKey:(id)aKey beforeObjectWithKey:(id)anotherKey;
- (void)setObject:(id)object forKey:(id)aKey;
- (void)setObject:(id)object atIndex:(NSUInteger)index;
- (void)removeObject:(id)object;
- (void)removeObjectForKey:(id)aKey;
- (void)removeObjectsForKeys:(NSArray *)keys;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)removeObjectsAtIndexes:(NSArray *)indexes;
- (void)removeAllObjects;
- (void)removeLastObject;
- (NSUInteger)count;
- (BOOL)isEqualToOrderedDictionary:(AXMutableOrderedDictionary *)dictionary;
- (void)exchangeObjectAtIndex:(NSUInteger)fromIndex withObjectAtIndex:(NSUInteger)toIndex;
- (NSEnumerator *)keyEnumerator;
- (NSEnumerator *)reverseKeyEnumerator;
- (void)enumerateObjectsUsingBlock:(void (^)(id key, id obj, BOOL * stop))block;
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, NSUInteger idx, BOOL * stop))block;
// NSMutableCopying
// - (id)mutableCopyWithZone:(NSZone *)zone;
// - (id)mutableCopy;
@end
#import "AXMutableOrderedDictionary.h"
#define REMOVE_DUPES(keyList, aKey) if ([keyList containsObject:aKey]) { \
[_keys removeObject:aKey]; \
}
// via [1] http://cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html
NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent)
{
NSString *objectString;
if ([object isKindOfClass:[NSString class]])
{
objectString = (NSString *)[[object retain] autorelease];
}
else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)])
{
objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent];
}
else if ([object respondsToSelector:@selector(descriptionWithLocale:)])
{
objectString = [(NSSet *)object descriptionWithLocale:locale];
}
else
{
objectString = [object description];
}
return objectString;
}
@interface AXMutableOrderedDictionary ()
@property (nonatomic, retain) NSMutableArray * _keys;
@property (nonatomic, retain) NSMutableDictionary * _dict;
@end
// TODO: Check standard classes for better assert error strings
@implementation AXMutableOrderedDictionary
@synthesize _keys;
@synthesize _dict;
+ (id)new
{
return [[self alloc] init];
}
+ (id)newWithCapacity:(NSUInteger)initialCapacity
{
return [[self alloc] initWithCapacity:initialCapacity];
}
+ (id)newWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary
{
return [[self alloc] initWithOrderedDictionary:otherDictionary];
}
+ (id)newWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
{
return [[self alloc] initWithObjects:objects forKeys:keys];
}
+ (id)orderedDictionary
{
return [[self new] autorelease];
}
+ (id)orderedDictionaryWithCapacity:(NSUInteger)initialCapacity
{
return [[self newWithCapacity:initialCapacity] autorelease];
}
+ (id)orderedDictionaryWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary
{
return [[self newWithOrderedDictionary:otherDictionary] autorelease];
}
+ (id)orderedDictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
{
return [[self newWithObjects:objects forKeys:keys] autorelease];
}
- (id)init
{
if ((self = [super init])) {
_keys = [[NSMutableArray alloc] init];
_dict = [[NSMutableDictionary alloc] init];
}
return self;
}
- (id)initWithCapacity:(NSUInteger)initialCapacity
{
if ((self = [super init])) {
_keys = [[NSMutableArray alloc] initWithCapacity:initialCapacity];
_dict = [[NSMutableDictionary alloc] initWithCapacity:initialCapacity];
}
return self;
}
- (id)initWithOrderedDictionary:(AXMutableOrderedDictionary *)otherDictionary
{
if ((self = [super init])) {
_keys = [otherDictionary._keys mutableCopy];
_dict = [otherDictionary._dict mutableCopy];
}
return self;
}
- (id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
{
NSAssert([objects count] == [keys count], @"Cannot initialize with different number of keys and objects");
if ((self = [super init])) {
_keys = [keys mutableCopy];
_dict = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys];
}
return self;
}
- (void)dealloc
{
[_keys release];
[_dict release];
[super dealloc];
}
#pragma mark - Alternative representations
- (NSDictionary *)dictionaryRepresentation
{
return [NSDictionary dictionaryWithDictionary:_dict];
}
- (NSArray *)arrayRepresentation
{
return [_dict allValues];
}
- (NSArray *)keys
{
return [NSArray arrayWithArray:_keys];
}
#pragma mark - Getting keys and indexes
- (id)keyForObject:(id)object
{
__block id foundKey = nil;
[_dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop) {
if ([object isEqual:obj]) {
foundKey = [key retain];
*stop = YES;
}
}];
return [foundKey autorelease];
}
- (id)keyForObjectAtIndex:(NSUInteger)index
{
return [_keys objectAtIndex:index];
}
- (NSUInteger)indexOfObject:(id)object
{
return [_keys indexOfObject:[self keyForObject:object]];
}
- (NSUInteger)indexOfObjectWithKey:(id)aKey
{
return [_keys indexOfObject:aKey];
}
#pragma mark - Getting objects
- (id)objectForKey:(id)aKey
{
return [_dict objectForKey:aKey];
}
- (id)objectAtIndex:(NSUInteger)index
{
return [_dict objectForKey:[_keys objectAtIndex:index]];
}
- (id)lastObject
{
return [_dict objectForKey:[_keys lastObject]];
}
#pragma mark - Adding objects
- (void)addObject:(id)object withKey:(id)aKey
{
REMOVE_DUPES(_keys, aKey);
[_keys addObject:aKey];
[_dict setObject:object forKey:aKey];
}
- (void)addObjects:(NSArray *)objects withKeys:(NSArray *)keys
{
NSAssert([objects count] == [keys count], @"Cannot add objects if all arrays are not of equal length.");
NSUInteger index;
for (index = 0; index < [objects count]; index++) {
[self addObject:[objects objectAtIndex:index] withKey:[keys objectAtIndex:index]];
}
}
- (void)insertObject:(id)object withKey:(id)aKey atIndex:(NSUInteger)index
{
REMOVE_DUPES(_keys, aKey);
[_keys insertObject:aKey atIndex:index];
[_dict setObject:object forKey:aKey];
}
- (void)insertObject:(id)object withKey:(id)aKey afterObject:(id)anotherObject
{
NSUInteger index = [self indexOfObject:anotherObject];
if (index != NSNotFound) {
[self insertObject:object withKey:aKey atIndex:index+1];
}
}
- (void)insertObject:(id)object withKey:(id)aKey beforeObject:(id)anotherObject
{
NSUInteger index = [self indexOfObject:anotherObject];
if (index != NSNotFound) {
[self insertObject:object withKey:aKey atIndex:index];
}
}
- (void)insertObject:(id)object withKey:(id)aKey afterObjectWithKey:(id)anotherKey
{
NSUInteger index = [_keys indexOfObject:anotherKey];
if (index != NSNotFound) {
[self insertObject:object withKey:aKey atIndex:index+1];
}
}
- (void)insertObject:(id)object withKey:(id)aKey beforeObjectWithKey:(id)anotherKey
{
NSUInteger index = [_keys indexOfObject:anotherKey];
if (index != NSNotFound) {
[self insertObject:object withKey:aKey atIndex:index];
}
}
#pragma mark - Setting objects
- (void)setObject:(id)object forKey:(id)aKey
{
[_dict setObject:object forKey:aKey];
}
- (void)setObject:(id)object atIndex:(NSUInteger)index
{
[self setObject:object forKey:[_keys objectAtIndex:index]];
}
- (void)setDictionary:(AXMutableOrderedDictionary *)dictionary
{
[_keys release];
_keys = [dictionary._keys retain];
[_dict release];
_dict = [dictionary._dict retain];
}
#pragma mark - Removing objects
- (void)removeObject:(id)object
{
NSString * aKey = [self keyForObject:object];
[_keys removeObject:aKey];
[_dict removeObjectForKey:aKey];
}
- (void)removeObjectForKey:(id)aKey
{
[_keys removeObject:aKey];
[_dict removeObjectForKey:aKey];
}
- (void)removeObjectsForKeys:(NSArray *)keys
{
for (id aKey in keys) {
[self removeObjectForKey:aKey];
}
}
- (void)removeObjectAtIndex:(NSUInteger)index
{
NSString * aKey = [_keys objectAtIndex:index];
[_dict removeObjectForKey:aKey];
[_keys removeObjectAtIndex:index];
}
- (void)removeObjectsAtIndexes:(NSArray *)indexes
{
for (NSNumber * index in indexes) {
[self removeObjectAtIndex:[index unsignedIntegerValue]];
}
}
- (void)removeAllObjects
{
[_keys removeAllObjects];
[_dict removeAllObjects];
}
- (void)removeLastObject
{
NSString * aKey = [_keys lastObject];
[_dict removeObjectForKey:aKey];
[_keys removeLastObject];
}
#pragma mark - Structure info
- (NSUInteger)count
{
NSAssert([_keys count] == [_dict count], @"Internal data stores are out of sync. Were the collections mutated direclty?");
return [_keys count];
}
- (NSString *)description
{
NSMutableString * description = [NSMutableString new];
[description appendFormat:@"%@\t{\n", [super description]];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, NSUInteger idx, BOOL * stop) {
[description appendFormat:@"\t%@ = %@;\n", key, obj];
}];
[description appendFormat:@"}"];
return [[description copy] autorelease];
}
// via [1]
- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
NSMutableString *indentString = [NSMutableString string];
NSUInteger i, count = level;
for (i = 0; i < count; i++)
{
[indentString appendFormat:@" "];
}
NSMutableString *description = [NSMutableString string];
[description appendFormat:@"%@{\n", indentString];
for (NSObject *key in self)
{
[description appendFormat:@"%@ %@ = %@;\n",
indentString,
DescriptionForObject(key, locale, level),
DescriptionForObject([self objectForKey:key], locale, level)];
}
[description appendFormat:@"%@}\n", indentString];
return description;
}
- (NSUInteger)hash
{
return [_keys hash] + [_dict hash];
}
- (BOOL)isEqual:(id)object
{
if (object == self) {
return YES; // Early out
}
if ([object class] == [self class]) {
return [self isEqualToOrderedDictionary:(AXMutableOrderedDictionary *)object];
}
return NO;
}
- (BOOL)isEqualToOrderedDictionary:(AXMutableOrderedDictionary *)dictionary
{
if (dictionary == self) {
return YES; // Early out
}
return [_keys isEqualToArray:dictionary._keys] && [_dict isEqualToDictionary:dictionary._dict];
}
#pragma mark - Reordering and sorting
- (void)exchangeObjectAtIndex:(NSUInteger)fromIndex withObjectAtIndex:(NSUInteger)toIndex
{
[_keys exchangeObjectAtIndex:fromIndex withObjectAtIndex:toIndex];
}
- (void)sortKeysUsingDescriptors:(NSArray *)sortDescriptors
{
[_keys sortUsingDescriptors:sortDescriptors];
}
#pragma mark - Enumeration
- (NSEnumerator *)keyEnumerator
{
return [_keys objectEnumerator];
}
- (NSEnumerator *)reverseKeyEnumerator
{
return [_keys reverseObjectEnumerator];
}
- (void)enumerateObjectsUsingBlock:(void (^)(id key, id obj, BOOL * stop))block
{
[_dict enumerateKeysAndObjectsUsingBlock:block];
}
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, NSUInteger idx, BOOL * stop))block
{
[_keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
block(obj, [_dict objectForKey:obj], idx, stop);
}];
}
#pragma mark - Copying
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithOrderedDictionary:self];
}
- (id)copy
{
return [self mutableCopy];
}
- (id)mutableCopy
{
return [[[self class] alloc] initWithOrderedDictionary:self];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment