Создайте NSSet из NSArray на основе свойства

Как создать NSSet объектов из массива на основе свойства.

например Массив объектов, каждый из которых имеет сильную ссылку на свойство type, и в массиве существует несколько вхождений каждого типа. Как это можно превратить в NSSet, содержащее по одному объекту каждого типа.


person some_id    schedule 07.03.2013    source источник
comment
Вам нужен только первый экземпляр каждого типа? Или произвольный экземпляр? На самом деле это не очевидное отображение массива в множество.   -  person Seamus Campbell    schedule 07.03.2013
comment
Переберите массив и добавьте эти объекты в NSMutableSet.   -  person ZhangChn    schedule 07.03.2013
comment
Какой тип type? Это строка, число, объект?   -  person CodaFi    schedule 07.03.2013
comment
Я использую библиотечный метод для получения одного объекта из каждого вхождения значения свойства в массиве одного и того же типа объекта. Я сам написал один для создания набора, но мне было интересно, есть ли оптимизированный библиотечный метод.   -  person some_id    schedule 07.03.2013
comment
У меня есть строковое свойство объекта. И в массиве, содержащем объекты этого типа, я хочу извлечь один экземпляр для каждого другого значения строкового свойства.   -  person some_id    schedule 07.03.2013
comment
@ Helium3, пожалуйста, посмотрите мой комментарий под моим вопросом, чтобы подтвердить. Вы ищете полученный NSSet для хранения разных свойств (одного и того же объекта) или для хранения повторяющихся объектов с разными свойствами.   -  person Khaled Barazi    schedule 07.03.2013
comment
Да, напр. 4 объекта в массиве со свойствами типа (NSString *), установленными на @type1, @type2, @type1, @type3. Передача этого массива в библиотечный метод для возврата 3 объектов, содержащих три уникальных имени. Таким образом, вместо того, чтобы возвращать уникальные строки, должны быть возвращены объекты, содержащие уникальные строки. Я сам написал этот метод, но задавался вопросом, не используется ли уже оптимизированный библиотечный метод.   -  person some_id    schedule 07.03.2013
comment
Краткий ответ на ваш вопрос - нет. Нет библиотечного метода, чтобы делать то, что вы хотите. Какой бы метод вы ни написали, это то, что вам нужно. Если ваши объекты с одинаковым значением type возвращают YES из isEqual:, тогда все, что вам нужно сделать, это создать набор из массива и все готово. Но поскольку это не так, вы должны написать свою собственную обработку.   -  person rmaddy    schedule 07.03.2013


Ответы (3)


Словарь, по сути, уже имеет эту функциональность. Его ключи представляют собой набор, поэтому вы можете создать словарь для хранения объектов с ключами любого интересующего вас атрибута:

[NSDictionary dictionaryWithObjects:arrayOfObjects 
                            forKeys:[arrayOfObjects valueForKey:theAttribute]];

Если вы запросите у словаря allValues сейчас, у вас будет только один объект для каждого атрибута. Я должен отметить, что с этой процедурой более поздние объекты будут сохранены в пользу более ранних. Если порядок вашего исходного массива значителен, измените его перед созданием словаря.

На самом деле вы не можете поместить эти объекты в 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
person jscs    schedule 07.03.2013

Вы бы использовали:

NSSet* mySetWithUniqueItems= [NSSet setWithArray: yourArray];

Это должно работать независимо от типа объектов в вашем массиве и будет заполнять 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.

person Khaled Barazi    schedule 07.03.2013
comment
Я хотел бы получить набор одного и того же типа объекта на основе уникальных значений свойств. - person some_id; 07.03.2013
comment
@ReyGonzales, я считаю, что сложность здесь в том, что описанный выше метод создаст NSSet с уникальными объектами из массива. Helium стремится заполнить NSSet объектами, которые похожи на уровне объектов, но имеют разные свойства. - person Khaled Barazi; 07.03.2013
comment
Да, напр. 4 объекта в массиве со свойствами типа (NSString *), установленными на @type1, @type2, @type1, @type3. Передача этого массива в библиотечный метод для возврата 3 объектов, содержащих три уникальных имени. Таким образом, вместо того, чтобы возвращать уникальные строки, должны быть возвращены объекты, содержащие уникальные строки. Я сам написал этот метод, но задавался вопросом, не используется ли уже оптимизированный библиотечный метод. - person some_id; 07.03.2013
comment
О, вам не нужно, чтобы это было написано. Возможно, это следует указать в вашем вопросе. - person Rey Gonzales; 07.03.2013
comment
@ Helium3, поэтому вас, скорее всего, беспокоит не то, что NSSet будет содержать дубликаты экземпляра вашего класса (поскольку NSSet позаботится об этом автоматически в моем ответе), а то, что вы хотите убедиться, что полученный NSSet не включает разные экземпляры, но имеющие одно и то же основное свойство? - person Khaled Barazi; 07.03.2013
comment
Пожалуйста, смотрите обновление моего ответа, вы можете сделать это с дополнительной парой строк. - person Khaled Barazi; 07.03.2013

Я создал простую библиотеку под названием Linq to ObjectiveC, которая представляет собой набор методов, позволяющих проблема решается гораздо проще. В вашем случае вам нужен метод Linq-to-ObjectiveC Different:

NSSet* dictionary = [NSSet setWithArray:[sourceArray distinct:^id(id item) {
    return [item type] ;
}]];

Это возвращает набор, в котором каждый элемент имеет отдельное свойство type.

person ColinE    schedule 08.03.2013