Search code examples
iosarrayscomparatornssortdescriptor

Convert NSArray into Array of unique values sorted by frequency in Objective-C or Swift


I have an array of values where I would like to count the incidence of each unique value and then create a new array of unique value sorted by incidence.

For example, for the Array as follows:

 vals = {0,0,0,2,0,1,2}

I would like to end up with array of {0,2,1} since 0 is the most common value, 2 is the second most common and 1 is the least common. Note, the fact that these are numbers is irrelevant. They could just as easily be strings.

I thought I might be able to use an nssortdescriptor as in:

NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:@"wordCount" ascending:NO];
NSArray *sortedcats = [vals sortedArrayUsingDescriptors:@[sorter]];

However, this is not sorting them by incidence and it also does not filter out duplicates.

Thanks for any suggestions.


Solution

  • There's a couple of approaches

    Convert to a Set/NSSet then sort.

    Swift

    let vals = [0,0,0,2,0,1,2]
    let uniqued = Set(vals).sorted()
    

    That same method would also work with NSSet + NSSortDescriptor in Objective C but obviously a longer line to write.

    or a more classic Foundation approach

    let nsvals: NSArray = [3,0,0,2,0,1,2]
    let nsuniquedAndSorted = nsvals.value(forKeyPath: "@distinctUnionOfObjects.self")
    

    Which is kind of cool as @distinctUnionOfObjects also sorts for free. There's lots of clever key-value tricks in the same vein here.

    EDIT

    To sort by incidence an approach could be to count the uniques then resort on that basis.

    let sortedByIncidence = uniqued
        .map { outer in return (outer, vals.filter({$0 == outer}).count) }
        .sorted { $0.1 > $1.1 }
        .map { $0.0 }