Getting unique items from NSMutableArray

I have a question about Objective-C today involving NSMutableArray. Coming from a .net/c# background I'm having some trouble working with these things.

Let's say I have an object called "Song"

My song has 3 properties:

  • Title
  • Artist
  • Genre

I have a NSMutableArray or NSArray wich holds all my Song objects.

How would I go about trying to 'query' my array to get a new array with only (Unique) Artists or Genre's.

Where as in .net you would write a simple LINQ query with a DISTINCT clause, how would one solve this in Objective-C ? I'm guessing with predicates but am struggling to find a solution.

Thanks in advance.


Depending on what you mean by "unique artists," there are a couple of different solutions. If you just want to get all the artists and don't want any to appear more than once, just do [NSSet setWithArray:[songArray valueForKey:@"artist"]]. If you mean you want to set a list of all the songs which are the only song by their artist, you need to first find the artists who only do one song and then find the songs by those artists:

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];

You could also use:

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

Likewise, if you need to compare the entire object you could create a new readonly property that combines the values you want to match on (via a hash or otherwise) dynamically and compare on that:

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

NOTE: Keep in mind this returns the uniques values for the specified property, not the objects themselves. So it will be an array of NSString values, not Song values.

Or like this:

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

filterTags was not unique at first, but becomes unique after the operation.

This may not be directly applicable but it is a solution to creating a unique set of values... Assume you have a NSMutable array of events that have multiple duplicate entries. This array is named eventArray. The NSSet object can be used to trim that array and then repopulate that array as shown below...

NSSet *uniqueEvents = [NSSet setWithArray:eventArray];

[eventArray removeAllObjects];

[eventArray addObjectsFromArray:[uniqueEvents allObjects]];

Use NSOrderedSet instead of NSSet.

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

I've created a simple query API for Objective-C that makes this kind of task a lot easier. Using the Linq-to-ObjectiveC distinct method, retrieving songs with unique artists would involve the following:

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

This returns a list of song instances, each with a unique artist.

Need Your Help

NGinx SSL certificate authentication signed by intermediate CA (chain)

authentication ssl nginx ssl-certificate

I am trying to enable client certificate authentication in nginx where the certificates have been signed by an intermediate CA. I am able to get this working fine when using a certificate signed b...

JavaScript: How do I print a message to the error console?

javascript debugging

How can I print a message to the error console, preferably including a variable?