Уникальные объекты внутри массива Swift

У меня есть массив с пользовательскими объектами.

Я хотел бы поместить повторяющиеся объекты с повторяющимися свойствами:

let product = Product()
product.subCategory = "one"
let product2 = Product()
product2.subCategory = "two"
let product3 = Product()
product3.subCategory = "two"
let array = [product,product2,product3]

в этом случае, нажмите product2 or product3

5 ответов

Вы можете использовать Swift Set:

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

Вы должны сделать Product соответствовать Hashable (и, следовательно, Equatable), хотя:

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

И если Product был подклассом NSObject, вы должны переопределить isEqual:

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

Очевидно, измените те, которые отражают другие свойства, которые могут быть у вас в вашем классе. Например:

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
}


Вот расширение массива, чтобы вернуть уникальный список объектов на основе заданного ключа:

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
 }
}
</t></t:hashable>

используя это, вы можете это сделать

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

это имеет то преимущество, что не требует Hashable и может возвращать уникальный список на основе любого поля или комбинации


Если Product соответствует Equatable, где произведение равно основано на его подкатегории (и вы не заботитесь о порядке), вы можете добавить объекты в набор и взять массив из этого набора:

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

или

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


Если ваш класс соответствует протоколу Hashable и вы хотите сохранить исходный порядок массива, вы можете создать расширение следующим образом:

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
 }
}


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
*/

если элемент является частью набора или нет, не зависит от hashValue, это зависит от сравнения. если ваш продукт соответствует Hashable, он должен соответствовать Equatable. если вам нужно, чтобы создание набора зависело исключительно от подкатегории, сравнение должно зависеть исключительно от подкатегории. это может быть большой проблемой, если вам нужно сравнить ваши продукты по-другому.

licensed under cc by-sa 3.0 with attribution.