How do I get unique values from an array

11,000

Solution 1

An simple way is to create an NSSet from the array with duplicates. The result is a set which by definition stores only the unique objects. Then using NSSet's -allObjects method to convert the NSSet back to an NSArray. The only downside is you lose the original ordering by converting to an NSSet.

NSArray *uniqueArray = [[NSSet setWithArray:duplicateArray] allObjects];

If you need to preserve the ordering and can require 10.7+, you can use an NSOrderedSet.

NSArray *uniqueArray = [[NSOrderedSet orderedSetWithArray:duplicateArray] array];

Edit:

Thanks to Mattt Thompson in the WWDC 2013 Session 228 - Hidden Gems in Cocoa and Cocoa Touch, there is another way without creating an intermediate set.

NSArray *uniqueArray = [duplicateArray valueForKeyPath:@"@distinctUnionOfObjects.self"]

Solution 2

You can use the array's valueForKeyPath and set it to distinctUnionOfObjects.self.

NSArray *yearsArray = @[@"1943", @"1945", @"1948", @"1948", @"1948", @"1949"];
NSArray *uniqueYears = [yearsArray valueForKeyPath:@"@distinctUnionOfObjects.self"];
NSLog(@"uniqueYears: %@", uniqueYears);

Solution 3

You provided a NSLog:

unique: (
    (
    1941,
    1942,
    1943,
    1945,
    1948,
    1948,
    1948,
    1949
    ) 
) 

This is not an array of strings. This is an array with one object, which is, itself, an array. That suggests that yearStringArray has that same structure. Clearly your code (and the name of that variable) suggests you thought it should be otherwise.

You should either fix whatever created yearStringArray to actually create an array of strings, or change your code to reflect this structure of yearStringArray, e.g.,

for (yearString in yearStringArray[0]) 
{
    ....
}

Solution 4

Here is bug.

if (![processed containsObject:yearString] == NO)

You have to use

if ([processed containsObject:yearString] == NO)

or

if (![processed containsObject:yearString])
Share:
11,000

Related videos on Youtube

ICL1901
Author by

ICL1901

OLD programmer - from the 60s (1960s at least), stumbling aimlessly through xCode, Obj-C, PHP, mySQL, sqLite, (x)html, css, js, bbc, COBOL, exoplanets, slashdot, Apple. #SOreadytohelp

Updated on October 09, 2022

Comments

  • ICL1901
    ICL1901 over 1 year

    Please note this is for OSX, not for iOS.

    I have looked and tried some solutions in other SO questions, but none seem to work for me, hence this:

    I want to get a unique set of years out of an array.

    My code is this:

    NSMutableArray * unique = [NSMutableArray array];
            NSMutableSet * processed = [NSMutableSet set];
            for (yearString in yearStringArray) {
                if (![processed containsObject:yearString] == NO) {
                    [unique addObject:yearString];
                }
                [processed addObject:yearString];
            }
    
            NSLog(@"unique: %@",unique );
            NSLog(@"processed: %@",processed );
    
        }];
    
    The console returns:
    
    unique: (
            (
            1941,
            1942,
            1943,
            1945,
            1948,
            1948,
            1948,
            1949
        )
    
    processed: {(
            (
            1941,
            1942,
            1943,
            1945,
            1948,
            1948,
            1948,
            1949
        )
    )}
    

    I only want one 1948. I would appreciate any help.

    UPDATES: Thanks for the answers all. Keep em coming please!!!

    These are the code snips I've tried now:

    1.

    for (yearString in yearStringArray) {
                if ([processed containsObject:yearString] == NO) {
                    [unique addObject:yearString];
                }
                [processed addObject:yearString];
            }
    

    2.

       NSString *yearString = [[NSString alloc] init];
        NSArray *objectArray = [[NSArray alloc] init];
        [objectArray = objects copy];
    
        for (int j = 0; j <= objectArray.count; j++) {
            NSString *yearString = [objectArray valueForKey:@"year"];
            [yearStringArray addObject:yearString];
        }
    
    
        NSMutableArray * unique = [NSMutableArray array];
        NSMutableSet * processed = [NSMutableSet set];
    
    
        for (yearString in yearStringArray) {
    
            [unique addObject:yearString];
            [processed addObject:yearString];
        }
    
        NSArray *processed2 = [[NSSet setWithArray:unique] allObjects];
        NSLog(@"unique: %@",unique );
        NSLog(@"processed: %@",processed );
        NSLog(@"processed2: %@",processed2 );
        //[self setYears];
    }];
    

    3.

    NSString *yearString = [[NSString alloc] init];
        NSArray *objectArray = [[NSArray alloc] init];
        [objectArray = objects copy];
    
        for (int j = 0; j <= objectArray.count; j++) {
            yearString = [objectArray valueForKey:@"year"];
            [yearStringArray addObject:yearString];
        }
    
    
    
        NSArray *uniqueYears = [yearStringArray valueForKeyPath:@"@distinctUnionOfObjects.self"];
        NSLog(@"uniqueYears: %@", uniqueYears);
    
    
    }];
    
    
    
    }
    

    Still not getting unique years..

  • ICL1901
    ICL1901 over 10 years
    Andrew, thank you. I used your code (properly this time). The unordered set worked beautifully. Many thanks.
  • KamilPyc
    KamilPyc almost 10 years
    Also you can mention that "@distinctUnionOfObjects.self" don't preserve order