NSSet
プロパティに基づいて配列からオブジェクトを作成するにはどうすればよいですか。
たとえば、それぞれがプロパティへの強い参照を持つオブジェクトの配列type
、および各タイプの複数のオカレンスが配列に存在します。NSSet
これを各タイプの単一のオブジェクトを保持する方法に変えるにはどうすればよいでしょうか。
NSSet
プロパティに基づいて配列からオブジェクトを作成するにはどうすればよいですか。
たとえば、それぞれがプロパティへの強い参照を持つオブジェクトの配列type
、および各タイプの複数のオカレンスが配列に存在します。NSSet
これを各タイプの単一のオブジェクトを保持する方法に変えるにはどうすればよいでしょうか。
NSSet *distinctSet = [NSSet setWithArray:[array valueForKeyPath:@"@distinctUnionOfObjects.property"]];
辞書には基本的に、この機能が既に備わっています。そのキーはセットなので、興味のある属性によってキー付けされたオブジェクトを保持する辞書を作成できます。
[NSDictionary dictionaryWithObjects:arrayOfObjects
forKeys:[arrayOfObjects valueForKey:theAttribute]];
とりあえず辞書を引くと、allValues
属性ごとに 1 つのオブジェクトしかありません。この手順では、後のオブジェクトが優先されて保持されることに注意してください。元の配列の順序が重要な場合は、ディクショナリを作成する前に逆にします。
NSSet
これらのオブジェクトを に実際に配置することはできません。 は、キー属性ではなくNSSet
、オブジェクトのisEqual:
およびメソッドを使用して、それらがメンバーであるかどうかを判断するためです (もちろん、これが独自のクラスである場合は、これらのメソッドをオーバーライドできますが、hash
他のコレクションでの動作に干渉する可能性があります)。
セットが本当に必要だと感じている場合は、独自のクラスを作成する必要があります。をサブクラス化できますがNSSet
、Cocoa コレクションの構成はサブクラス化よりもはるかに簡単であるというのが一般通念です。基本的に、興味のあるセットメソッドをカバーするクラスを作成します。これは(かなり不完全で完全にテストされていない)スケッチです。
@interface KeyedMutableSet : NSObject
/* This selector is performed on any object which is added to the set.
* If the result already exists, then the object is not added.
*/
@property (assign, nonatomic) SEL keySEL;
- (id)initWithKeySEL:(SEL)keySEL;
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL;
- (void)addObject:(id)obj;
- (NSArray *)allObjects;
- (NSArray *)allKeys;
- (BOOL)containsObject:(id)obj;
- (NSUInteger)count;
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block;
// And so on...
@end
#import "KeyedMutableSet.h"
@implementation KeyedMutableSet
{
NSMutableArray * _objects;
NSMutableSet * _keys;
}
- (id)initWithKeySEL:(SEL)keySEL
{
return [self initWithArray:nil usingKeySEL:keySEL];
}
- (id)initWithArray:(NSArray *)initArray usingKeySEL:(SEL)keySEL
{
self = [super init];
if( !self ) return nil;
_keySEL = keySEL;
_objects = [NSMutableArray array];
_keys = [NSMutableSet set];
for( id obj in initArray ){
[self addObject:obj];
}
return self;
}
- (void)addObject:(id)obj
{
id objKey = [obj performSelector:[self keySEL]];
if( ![keys containsObject:objKey] ){
[_keys addObject:objKey];
[_objects addObject:obj];
}
}
- (NSArray *)allObjects
{
return _objects;
}
- (NSArray *)allKeys
{
return [_keys allObjects];
}
- (BOOL)containsObject:(id)obj
{
return [_keys containsObject:[obj performSelector:[self keySEL]]];
}
- (NSUInteger)count
{
return [_objects count];
}
- (NSString *)description
{
return [_objects description];
}
-(void)enumerateObjectsUsingBlock:(void (^)(id, BOOL *))block
{
for( id obj in _objects ){
BOOL stop = NO;
block(obj, &stop);
if( stop ) break;
}
}
@end
私はLinqtoObjectiveCと呼ばれる単純なライブラリを作成しました。これは、この種の問題をはるかに簡単に解決できるようにするメソッドのコレクションです。あなたの場合、Linq-to-ObjectiveCの個別のメソッドが必要です。
NSSet* dictionary = [NSSet setWithArray:[sourceArray distinct:^id(id item) {
return [item type] ;
}]];
これは、各アイテムが個別のtype
プロパティを持つセットを返します。
NSMutableSet* classes = [[NSMutableSet alloc] init];
NSMutableSet* actualSet = [[NSMutableSet alloc] init];
for(id object in array) {
if([classes containsObject:[object class]] == NO) {
[classes addObject:[object class]];
[actualSet addObject:object];
}
}
以下を使用します。
NSSet* mySetWithUniqueItems= [NSSet setWithArray: yourArray];
これは、配列内のオブジェクトのタイプに関係なく機能し、配列内の重複オブジェクトが 1 つだけ出現するように NSSet を設定します。
これが役立つことを願っています。
更新:次善の策:最初にクラス名とオブジェクトプロパティの連結を使用してから、上記の方法を使用します。
self.concatenatedArray=[NSMutableArray arrayWithCapacity:4];
for (TheClass* object in self.myArray)
[self.concatenatedArray addObject:[NSString stringWithFormat:@"%@-%@",[object class], object.theProperty]];
self.mySet=[NSSet setWithArray:self.concatenatedArray];
NSSet 出力を何に使用するかはわかりませんが、おそらく連結要素を変更して、必要な情報を NSSet 出力に含めることができます。