Craig Otis
Craig Otis

Reputation: 32054

Collect number of unique property values in Swift array of objects

Say I have a simple Ingredient class:

class Ingredient {
    let section : String // 'Produce', 'Spices', 'Dairy', 'Grains'
}

And I have a pile of Ingredient items:

var ingredients : [Ingredient] = ...

I want to collect the number of distinct sections from these ingredients.

I can do this in Java via (relying on the auto-clumping of Set types):

ingredients.stream().map(Ingredient::getSection).collect(Collectors.toSet()).count()

Or, using the distinct() method:

ingredients.stream().map(Ingredient::getSection).distinct().count()

But I'm looking for a way to do a similar one-liner in Swift. Some of the research I've done shows people writing their own methods to collect distinct values, but I was hoping there would be a distinct() or Set-collecting method for Swift types.

Upvotes: 3

Views: 2561

Answers (4)

Cătălin
Cătălin

Reputation: 23

Using Swift 3, and keeping O(n) complexity it looks like this:

extension Array where Element: Hashable {

    func distinct() -> Array<Element> {
        return Array(Set(self))
    }
}

Upvotes: 2

Sulthan
Sulthan

Reputation: 130102

It's the same mostly, you should use the Set type:

let ingredients = [Ingredient(section: "Produce"), Ingredient(section: "Produce"), Ingredient(section: "Spices"), Ingredient(section: "Dairy")]

let sections = Set(ingredients.map{ $0.section })
let numDistinctSections = Set(ingredients.map{ $0.section }).count

Also, nothing simpler than declaring your own distinct function:

extension CollectionType where Generator.Element : Hashable {
    func distinct() -> [Generator.Element] {
        return Array(Set(self))
    }
}

let sections = ingredients.map{ $0.section }.distinct()

Upvotes: 4

Casey Fleser
Casey Fleser

Reputation: 5787

Sure. Turn your sections into a [String], use that to create a Set and get the count like this:

let numUnique = Set(ingredients.map({ $0.section })).count

Upvotes: 2

Hamish
Hamish

Reputation: 80811

Sure, you can do this in much the same way by mapping over your ingredients array to extract an array of sections – you can then convert these to a Set and get out the count, giving you the number of distinct sections.

let distinctSections = Set(ingredients.map{$0.section}).count

Upvotes: 8

Related Questions