Unique Objects inside a Array Swift

后端 未结 6 2132
小鲜肉
小鲜肉 2021-02-03 13:43

I have an array, with custom objects.

I Would like to pop the repeated objects, with the repeated properties:

let product = Product()
product.subCategory         


        
相关标签:
6条回答
  • 2021-02-03 13:49

    You can use Swift Set:

    let array = [product,product2,product3]
    
    let set = Set(array)
    

    You have to make Product conform to Hashable (and thus, Equatable) though:

    class Product : Hashable {
        var subCategory = ""
    
        var hashValue: Int { return subCategory.hashValue }
    }
    
    func ==(lhs: Product, rhs: Product) -> Bool {
        return lhs.subCategory == rhs.subCategory
    }
    

    And, if Product was a NSObject subclass, you have to override isEqual:

    override func isEqual(object: AnyObject?) -> Bool {
        if let product = object as? Product {
            return product == self
        } else {
            return false
        }
    }
    

    Clearly, modify those to reflect other properties you might have in your class. For example:

    class Product : Hashable {
        var category = ""
        var subCategory = ""
    
        var hashValue: Int { return [category, subCategory].hashValue }
    }
    
    func ==(lhs: Product, rhs: Product) -> Bool {
        return lhs.category == rhs.category && lhs.subCategory == rhs.subCategory
    }
    
    0 讨论(0)
  • 2021-02-03 13:55

    Here is an Array extension to return the unique list of objects based on a given key:

    extension Array {
        func unique<T:Hashable>(map: ((Element) -> (T)))  -> [Element] {
            var set = Set<T>() //the unique list kept in a Set for fast retrieval
            var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
            for value in self {
                if !set.contains(map(value)) {
                    set.insert(map(value))
                    arrayOrdered.append(value)
                }
            }
    
            return arrayOrdered
        }
    }
    

    using this you can so this

    let unique = [product,product2,product3].unique{$0.subCategory}
    

    this has the advantage of not requiring the Hashable and being able to return an unique list based on any field or combination

    0 讨论(0)
  • 2021-02-03 13:56

    Here is a KeyPath based version of the Ciprian Rarau' solution

    extension Array {
        func unique<T: Hashable>(by keyPath: KeyPath<Element, T>) -> [Element] {
            var set = Set<T>()
            return self.reduce(into: [Element]()) { result, value in
                guard !set.contains(value[keyPath: keyPath]) else {
                    return
                }
                set.insert(value[keyPath: keyPath])
                result.append(value)
            }
        }
    }
    

    example usage:

    let unique = [product, product2, product3].unique(by: \.subCategory)
    
    0 讨论(0)
  • 2021-02-03 14:06

    If Product conforms to Equatable, where a product is equal based on it's subcategory (and you don't care about order), you can add the objects to a set, and take an array from that set:

    let array = [product,product2,product3]
    let set = NSSet(array: array)
    let uniqueArray = set.allObjects
    

    or

    let array = [product,product2,product3]
    let set = Set(array)
    let uniqueArray = Array(set)
    
    0 讨论(0)
  • 2021-02-03 14:09
    class Product {
        var subCategory: String = ""
    }
    
    let product = Product()
    product.subCategory = "one"
    
    let product2 = Product()
    product2.subCategory = "two"
    
    let product3 = Product()
    product3.subCategory = "two"
    
    let array = [product,product2,product3]
    
    extension Product : Hashable {
        var hashValue: Int {
            return subCategory.hashValue
        }
    }
    func ==(lhs: Product, rhs: Product)->Bool {
        return lhs.subCategory == rhs.subCategory
    }
    
    let set = Set(array)
    set.forEach { (p) -> () in
        print(p, p.subCategory)
    }
    /*
    Product one
    Product two
    */
    

    if an item is part of set or not doesn't depends on hashValue, it depends on comparation. if your product conform to Hashable, it should conform to Equatable. if you need that the creation of the set depends solely on subCategory, the comparation should depends solely on subCategory. this can be a big trouble, if you need to compare your products some other way

    0 讨论(0)
  • 2021-02-03 14:15

    If your class conforms to protocol Hashable and you would like to keep the original array order you can create an extension as follow:

    extension Array where Element: Hashable {
        var uniqueElements: [Element] {
            var elements: [Element] = []
            for element in self {
                if let _ = elements.indexOf(element) {
                    print("item found")
                } else {
                    print("item not found, add it")
                    elements.append(element)
                }
            }
            return elements
        }
    }
    
    0 讨论(0)
提交回复
热议问题