Index: src/OFArray.h ================================================================== --- src/OFArray.h +++ src/OFArray.h @@ -194,10 +194,41 @@ * @return The object at the specified index in the array */ - (ObjectType)objectAtIndex: (size_t)index; - (ObjectType)objectAtIndexedSubscript: (size_t)index; +/*! + * @brief Returns the value for the specified key + * + * If the key starts with an `@`, the `@` is stripped and + * `[super valueForKey:]` is called. + * If the key does not start with an `@`, a new array with the value for the + * specified key for each object is returned. + * + * @note Any nil values are replaced with @ref OFNull! + * + * @param key The key of the value to return + * @return The value for the specified key + */ +- (nullable id)valueForKey: (OFString*)key; + +/*! + * @brief Set the value for the specified key + * + * If the key starts with an `@`, the `@` is stripped and + * `[super setValue:forKey:]` is called. + * If the key does not start with an `@`, @ref setValue:forKey: is called for + * each object. + * + * @note A @ref OFNull value is translated to nil! + * + * @param value The value for the specified key + * @param key The key of the value to set + */ +- (void)setValue: (nullable id)value + forKey: (OFString*)key; + /*! * @brief Copies the objects at the specified range to the specified buffer. * * @param buffer The buffer to copy the objects to * @param range The range to copy Index: src/OFArray.m ================================================================== --- src/OFArray.m +++ src/OFArray.m @@ -25,10 +25,11 @@ #import "OFArray_subarray.h" #import "OFArray_adjacent.h" #import "OFString.h" #import "OFXMLElement.h" #import "OFDataArray.h" +#import "OFNull.h" #import "OFEnumerationMutationException.h" #import "OFInvalidArgumentException.h" #import "OFOutOfRangeException.h" @@ -272,10 +273,39 @@ - (id)objectAtIndexedSubscript: (size_t)index { return [self objectAtIndex: index]; } + +- (id)valueForKey: (OFString*)key +{ + OFMutableArray *ret = [OFMutableArray arrayWithCapacity: [self count]]; + + for (id object in self) { + id value = [object valueForKey: key]; + + if (value == nil) + value = [OFNull null]; + + [ret addObject: value]; + } + + [ret makeImmutable]; + + return ret; +} + +- (void)setValue: (id)value + forKey: (OFString*)key +{ + if (value == [OFNull null]) + value = nil; + + for (id object in self) + [object setValue: value + forKey: key]; +} - (size_t)indexOfObject: (id)object { size_t i = 0; Index: src/OFKeyValueCoding.h ================================================================== --- src/OFKeyValueCoding.h +++ src/OFKeyValueCoding.h @@ -28,11 +28,11 @@ * Key Value Coding makes it possible to access properties dynamically using * the interface described by this protocol. */ @protocol OFKeyValueCoding /*! - * @brief Return the value for the specified key + * @brief Returns the value for the specified key * * @param key The key of the value to return * @return The value for the specified key */ - (nullable id)valueForKey: (OFString*)key; Index: tests/OFArrayTests.m ================================================================== --- tests/OFArrayTests.m +++ tests/OFArrayTests.m @@ -16,10 +16,12 @@ #include "config.h" #import "OFArray.h" #import "OFString.h" +#import "OFNumber.h" +#import "OFURL.h" #import "OFAutoreleasePool.h" #import "OFEnumerationMutationException.h" #import "OFOutOfRangeException.h" @@ -293,11 +295,11 @@ } return nil; }]) && [[m[0] description] isEqual: @"(\n\tfoo,\n\tbar\n)"]) - TEST(@"-[mappedArrayUsingBLock]", + TEST(@"-[mappedArrayUsingBlock:]", [[[m[0] mappedArrayUsingBlock: ^ id (id obj, size_t idx) { switch (idx) { case 0: return @"foobar"; case 1: @@ -318,8 +320,26 @@ [left appendString: right]; return left; }]) #endif + TEST(@"-[valueForKey:]", + [[[OFArray arrayWithObjects: @"foo", @"bar", @"quxqux", nil] + valueForKey: @"length"] isEqual: + [OFArray arrayWithObjects: [OFNumber numberWithSize: 3], + [OFNumber numberWithSize: 3], [OFNumber numberWithSize: 6], nil]]) + + m[0] = [OFMutableArray arrayWithObjects: + [OFURL URLWithString: @"http://foo.bar/"], + [OFURL URLWithString: @"http://bar.qux/"], + [OFURL URLWithString: @"http://qux.quxqux/"], nil]; + TEST(@"-[setValue:forKey:]", + R([m[0] setValue: [OFNumber numberWithShort: 1234] + forKey: @"port"]) && + [m[0] isEqual: [OFArray arrayWithObjects: + [OFURL URLWithString: @"http://foo.bar:1234/"], + [OFURL URLWithString: @"http://bar.qux:1234/"], + [OFURL URLWithString: @"http://qux.quxqux:1234/"], nil]]) + [pool drain]; } @end