For arrays where the elements are neither Hashable nor Comparable (e.g. complex objects, dictionaries or structs), this extension provides a generalized way to remove duplicates:
extension Array
{
func filterDuplicate<T:Hashable>(_ keyValue:(Element)->T) -> [Element]
{
var uniqueKeys = Set<T>()
return filter{uniqueKeys.insert(keyValue($0)).inserted}
}
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
{
return filterDuplicate{"\(keyValue($0))"}
}
}
// example usage: (for a unique combination of attributes):
peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }
or...
peopleArray = peopleArray.filterDuplicate{ "\(($0.name, $0.age, $0.sex))" }
You don't have to bother with making values Hashable and it allows you to use different combinations of fields for uniqueness.
Note: for a more robust approach, please see the solution proposed by Coeur in the comments below.
stackoverflow.com/a/55684308/1033581
[EDIT] Swift 4 alternative
With Swift 4.2 you can use the Hasher class to build a hash much easier. The above extension could be changed to leverage this :
extension Array
{
func filterDuplicate(_ keyValue:((AnyHashable...)->AnyHashable,Element)->AnyHashable) -> [Element]
{
func makeHash(_ params:AnyHashable ...) -> AnyHashable
{
var hash = Hasher()
params.forEach{ hash.combine($0) }
return hash.finalize()
}
var uniqueKeys = Set<AnyHashable>()
return filter{uniqueKeys.insert(keyValue(makeHash,$0)).inserted}
}
}
The calling syntax is a little different because the closure receives an additional parameter containing a function to hash a variable number of values (which must be Hashable individually)
peopleArray = peopleArray.filterDuplicate{ $0($1.name, $1.age, $1.sex) }
It will also work with a single uniqueness value (using $1 and ignoring $0).
peopleArray = peopleArray.filterDuplicate{ $1.name }