Ispirato da https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-swift , possiamo dichiarare uno strumento più potente che è in grado di filtrare per unicità su qualsiasi keyPath. Grazie ai commenti di Alexander su varie risposte riguardanti la complessità, le soluzioni seguenti dovrebbero essere quasi ottimali.
Soluzione non mutante
Estendiamo con una funzione che è in grado di filtrare per unicità su qualsiasi keyPath:
extension RangeReplaceableCollection {
/// Returns a collection containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> Self {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
Nota: nel caso in cui il tuo oggetto non sia conforme a RangeReplaceableCollection, ma sia conforme a Sequence, puoi avere questa estensione aggiuntiva, ma il tipo restituito sarà sempre un array:
extension Sequence {
/// Returns an array containing, in order, the first instances of
/// elements of the sequence that compare equally for the keyPath.
func unique<T: Hashable>(for keyPath: KeyPath<Element, T>) -> [Element] {
var unique = Set<T>()
return filter { unique.insert($0[keyPath: keyPath]).inserted }
}
}
uso
Se vogliamo l'unicità per gli elementi stessi, come nella domanda, usiamo keyPath \.self
:
let a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let b = a.unique(for: \.self)
/* b is [1, 4, 2, 6, 24, 15, 60] */
Se vogliamo unicità per qualcos'altro (come per la id
raccolta di oggetti), utilizziamo il keyPath di nostra scelta:
let a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
let b = a.unique(for: \.y)
/* b is [{x 1 y 1}, {x 1 y 2}] */
Soluzione mutante
Estendiamo con una funzione mutante in grado di filtrare l'unicità su qualsiasi keyPath:
extension RangeReplaceableCollection {
/// Keeps only, in order, the first instances of
/// elements of the collection that compare equally for the keyPath.
mutating func uniqueInPlace<T: Hashable>(for keyPath: KeyPath<Element, T>) {
var unique = Set<T>()
removeAll { !unique.insert($0[keyPath: keyPath]).inserted }
}
}
uso
Se vogliamo l'unicità per gli elementi stessi, come nella domanda, usiamo keyPath \.self
:
var a = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
a.uniqueInPlace(for: \.self)
/* a is [1, 4, 2, 6, 24, 15, 60] */
Se vogliamo unicità per qualcos'altro (come per la id
raccolta di oggetti), utilizziamo il keyPath di nostra scelta:
var a = [CGPoint(x: 1, y: 1), CGPoint(x: 2, y: 1), CGPoint(x: 1, y: 2)]
a.uniqueInPlace(for: \.y)
/* a is [{x 1 y 1}, {x 1 y 2}] */
NSSet
, NSSet è una raccolta non ordinata di oggetti, se è necessario mantenere l'ordine NSOrderedSet.