简体   繁体   中英

How is possible to a property in a Set of objects conform to Hashable protocol in Swift?

I'm implementing a tag feature for an item list. I'm trying implementing a computed property that calculate the tag set in a list of item as the union of different tag set of each item like:

item1 - [tag1, tag2]

item2 - [tag1, tag3]

output > [tag1, tag2, tag3]

The problem is that the Tag class need to be hashable and an UID is given at each instance of the tag, even tag with the same description. So when I loop in all the item taglist to create the tag set of the whole list the results is wrong like:

output > [tag1, tag1, tag2, tag3]

Here's the code:

class TTDItem: Identifiable {
    
    var id: UUID = UUID()
    var itemDesc: String
    var itemTags: Set<TTDTag>
    
    init(itemDesc: String, itemTags: Set<TTDTag>) {
        self.itemDesc = itemDesc
        self.itemTags = itemTags
    }
}

class TTDTag: Identifiable, Hashable {
    
    var TTDTagDesc: String
    var hashValue: Int {
        return id.hashValue
    }
    
    init(TTDTagDesc: String){
        self.TTDTagDesc = TTDTagDesc
    }
    
    static func ==(lhs: TTDTag, rhs: TTDTag) -> Bool {
        return lhs.id == rhs.id
    }
}

class TTDItemList {
    var itemList: [TTDItem]
    init(itemList: [TTDItem]) {
        self.itemList = itemList
    }
    //(...)
    // implement computed property taglist
    func itemTagsList()-> Set<TTDTag> {
        var tagSet = Set<TTDTag>()
        for item in self.itemList {
            tagSet = tagSet.union(item.itemTags)
        }
        return tagSet
    }
}

How can I access only to the tag description in order to obtain the correct result? Thanks

This can be done using reduce and the union function

func itemTagsList()-> Set<TTDTag> {
    itemList.map(\.itemTags).reduce(Set<TTDTag>()){ $0.union($1) }
}

Note that for Hashable you need to implement hash(into:) for TTDTag

func hash(into hasher: inout Hasher) {
    hasher.combine(TTDTagDesc)
} 

You should start property names with a lowercase letter and make them descriptive so maybe you could change TTDTagDesc to tagDescription

hashValue is deprecated (and you should have received a warning for this). You should override hash(into:) and use your TTDTagDesc property there.

Also, you should implement id to return TTDTagDesc , because that is what identifies a tag.

class TTDTag: Identifiable, Hashable {
    
    var TTDTagDesc: String

    // Note here
    func hash(into hasher: inout Hasher) {
        hasher.combine(TTDTagDesc)
    }
    
    // and here
    var id: String { TTDTagDesc }
    
    init(TTDTagDesc: String){
        self.TTDTagDesc = TTDTagDesc
    }
    
    static func ==(lhs: TTDTag, rhs: TTDTag) -> Bool {
        return lhs.id == rhs.id
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM