Получение уникальных элементов из NSMutableArray

У меня есть вопрос о Objective-C сегодня с участием NSMutableArray. Исходя из фона .net/С#, у меня возникают проблемы с этим.

Скажем, у меня есть объект под названием "Песня"

Моя песня имеет 3 свойства:

  • Название
  • Исполнитель
  • Жанр

У меня есть NSMutableArray или NSArray, в котором хранятся все мои объекты Song.

Как бы я попытался "запросить" мой массив, чтобы получить новый массив только с (уникальными) артистами или жанром.

Где, как и в .net, вы должны написать простой запрос LINQ с предложением DISTINCT, как решить эту проблему в Objective-C? Я догадываюсь с предикатами, но изо всех сил стараюсь найти решение.

Спасибо заранее.

Ответ 1

В зависимости от того, что вы подразумеваете под "уникальными художниками", существует несколько различных решений. Если вы просто хотите, чтобы все художники и не хотели появляться больше одного раза, просто сделайте [NSSet setWithArray:[songArray valueForKey:@"artist"]]. Если вы хотите указать список всех песен, которые являются единственной песней их исполнителя, вам нужно сначала найти художников, которые делают только одну песню, а затем найти песни этих художников:

NSCountedSet *artistsWithCounts = [NSCountedSet setWithArray:[songArray valueForKey:@"artist"]];
NSMutableSet *uniqueArtists = [NSMutableSet set];
for (id artist in artistsWithCounts)
    if ([artistsWithCounts countForObject:artist] == 1])
        [uniqueArtists addObject:artist];
NSPredicate *findUniqueArtists = [NSPredicate predicateWithFormat:@"artist IN %@", uniqueArtists];
NSArray *songsWithUniqueArtists = [songArray filteredArrayUsingPredicate:findUniqueArtists];

Ответ 2

Вы также можете использовать:

NSArray *uniqueArtists = [songs valueForKeyPath:@"@distinctUnionOfObjects.artist"];
NSArray *uniqueGenres = [songs valueForKeyPath:@"@distinctUnionOfObjects.genre"];

Аналогично, если вам нужно сравнить весь объект, вы можете создать новое свойство readonly, которое будет сочетать значения, которые вы хотите сопоставить (с помощью хэша или иначе), динамически и сравнить на них:

NSArray *array = [songs valueForKeyPath:@"@distinctUnionOfObjects.hash"];

ПРИМЕЧАНИЕ.. Помните, что это возвращает значения uniques для указанного свойства, а не сами объекты. Таким образом, это будет массив значений NSString, а не значений Song.

Ответ 3

Или вот так:

filterTags  = [[NSSet setWithArray:filterTags] allObjects];

filterTags сначала не был уникальным, но стал уникальным после операции.

Ответ 4

Это может быть неприменимо, но это решение для создания уникального набора значений... Предположим, что у вас есть NSMutable массив событий с несколькими повторяющимися записями. Этот массив называется eventArray. Объект NSSet можно использовать для обрезки этого массива и затем повторного заполнения массива, как показано ниже.

NSSet *uniqueEvents = [NSSet setWithArray:eventArray];

[eventArray removeAllObjects];

[eventArray addObjectsFromArray:[uniqueEvents allObjects]];

Ответ 5

Используйте NSOrderedSet вместо NSSet.

NSOrderedSet *uniqueOrderedSet = [NSOrderedSet setWithArray:itemsArray];
[itemsArray removeAllObjects];
[itemsArray addObjectsFromArray:[uniqueOrderedSet allObjects]];

Ответ 6

Я создал простой API запросов для Objective-C, который делает эту задачу намного проще. Используя метод Linq-to-ObjectiveC,, поиск песен с уникальными художниками будет включать в себя следующее:

NSArray* songsWithUniqueArtistists = [input distinct:^id(id song) {
    return [song artist];
}];

Это возвращает список экземпляров песен, каждый из которых имеет уникальный художник.