Suppression d'éléments dupliqués d'un tableau dans Swift
je pourrais avoir un tableau qui ressemble à ce qui suit:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
ou, en fait, n'importe quelle séquence de portions de données semblables dactylographiées. Ce que je veux faire, c'est m'assurer qu'il n'y a qu'un seul élément identique dans chaque cas. Par exemple, le tableau ci-dessus deviendrait:
[1, 4, 2, 6, 24, 15, 60]
noter que les duplicata de 2, 6, et 15 ont été enlevés pour s'assurer qu'il n'y avait qu'un de chaque élément identique. N'Swift fournir un moyen de le faire facilement, ou devrai-je le faire moi-même?
30 réponses
vous pouvez lancer les vôtres, par exemple comme ceci ( mis à jour pour Swift 1.2 avec L'ensemble ):
func uniq<S : SequenceType, T : Hashable where S.Generator.Element == T>(source: S) -> [T] {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(vals) // [1, 4, 2, 6, 24, 15, 60]
Swift 3 version:
func uniq<S : Sequence, T : Hashable>(source: S) -> [T] where S.Iterator.Element == T {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
vous pouvez convertir en un ensemble et revenir à un tableau à nouveau assez facilement:
let unique = Array(Set(originals))
ceci n'est pas garanti pour maintenir l'ordre original du tableau.
beaucoup de réponses disponibles ici, mais j'ai manqué cette extension simple, adapté pour Swift 2 et plus:
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
le rend super simple. Peut être appelé comme ceci:
let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
filtrage basé sur les propriétés
Pour filtrer un tableau basé sur les propriétés, vous pouvez utiliser cette méthode:
extension Array {
func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(lhs: element, rhs: "151920920")
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
que vous pouvez appeler comme suit:
let filteredElements = myElements.filterDuplicates { "151930920".PropertyOne == .PropertyOne && "151930920".PropertyTwo == .PropertyTwo }
ceci prend une partie de la bonne information qui est déjà sur cette page, et applique l'approche Hashable/Set lorsque possible, et retombe au code équivalent autrement.
Swift 4 changement pour la Equatable
extension ( Hashable
reste le même)
public extension Sequence where Element: Equatable {
var uniqueElements: [Element] {
return self.reduce(into: []) {
uniqueElements, element in
if !uniqueElements.contains(element) {
uniqueElements.append(element)
}
}
}
}
Swift 3
public extension Sequence where Iterator.Element: Hashable {
var uniqueElements: [Iterator.Element] {
return Array( Set(self) )
}
}
public extension Sequence where Iterator.Element: Equatable {
var uniqueElements: [Iterator.Element] {
return self.reduce([]){
uniqueElements, element in
uniqueElements.contains(element)
? uniqueElements
: uniqueElements + [element]
}
}
}
Swift 2
public extension SequenceType where Generator.Element: Hashable {
var uniqueElements: [Generator.Element] {
return Array(
Set(self)
)
}
}
public extension SequenceType where Generator.Element: Equatable {
var uniqueElements: [Generator.Element] {
return self.reduce([]){uniqueElements, element in
uniqueElements.contains(element)
? uniqueElements
: uniqueElements + [element]
}
}
}
Swift 3.0
let uniqueUnordered = Array(Set(array))
let uniqueOrdered = Array(NSOrderedSet(array: array))
Swift 4
garanti pour continuer la commande.
extension Array where Element: Equatable {
func removingDuplicates() -> Array {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
limite les éléments de la collection à Equatable que vous pouvez utiliser contient:
extension Collection where Element: Equatable {
var orderedSet: [Element] {
var array: [Element] = []
return compactMap {
if array.contains("151900920") {
return nil
} else {
array.append("151900920")
return "151900920"
}
}
}
}
une autre option est de forcer l'élément collection à Hashable et d'utiliser un ensemble pour contrôler quels éléments vous devez mapper dans le résultat:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return compactMap { set.insert("151910920").inserted ? "151910920" : nil }
}
}
utilisant un filtre:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return filter { set.insert("151920920").inserted }
}
}
ou en utilisant NSOrderedSet:
extension Array where Element: Hashable {
var orderedSet: Array {
return NSOrderedSet(array: self).array as? Array ?? []
}
}
utiliser Swift 4 réduire(en:)
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set: Set<Element> = []
return reduce(into: []) { set.insert().inserted ? "151940920".append() : () }
}
}
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Swift 4
public extension Array where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter{ seen.insert("151900920").inserted }
}
}
toute tentative de insert
retournera aussi un tuple: (inserted: Bool, memberAfterInsert: Set.Element)
. Voir documentation .
L'utilisation de la valeur retournée nous aide à éviter la boucle ou de faire toute autre opération.
une solution alternative (sinon optimale) de ici utilisant des types immuables plutôt que des variables:
func deleteDuplicates<S: ExtensibleCollectionType where S.Generator.Element: Equatable>(seq:S)-> S {
let s = reduce(seq, S()){
ac, x in contains(ac,x) ? ac : ac + [x]
}
return s
}
incluait pour comparer l'approche impérative de Jean-Pillippe avec une approche fonctionnelle.
en bonus, Cette fonction fonctionne avec des chaînes aussi bien qu'avec des tableaux!
swift 2
avec uniq fonction Réponse:
func uniq<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter({ (v) -> Bool in
return seen.updateValue(true, forKey: v) == nil
})
}
utiliser:
var test = [1,2,3,4,5,6,7,8,9,9,9,9,9,9]
print(uniq(test)) //1,2,3,4,5,6,7,8,9
Voici une catégorie sur SequenceType
qui préserve l'ordre original du tableau, mais utilise un Set
pour faire les contains
les recherches pour éviter le O(n)
coût sur la méthode contains(_:)
.
public extension Sequence where Iterator.Element: Hashable {
public func unique() -> [Iterator.Element] {
var buffer: [Iterator.Element] = []
var lookup = Set<Iterator.Element>()
for element in self {
guard !lookup.contains(element) else { continue }
buffer.append(element)
lookup.insert(element)
}
return buffer
}
}
ou si vous n'avez pas de Hashable, vous pouvez le faire:
public extension Sequence where Iterator.Element: Equatable {
public func unique() -> [Iterator.Element] {
var buffer: [Iterator.Element] = []
for element in self {
guard !buffer.contains(element) else { continue }
buffer.append(element)
}
return buffer
}
}
vous pouvez coller les deux dans votre application, Swift choisira la bonne en fonction du type de séquence Iterator.Element
.
vous pouvez utiliser directement un ensemble de collecte pour supprimer le duplicata, puis le rejeter dans un tableau
var myArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var mySet = Set<Int>(myArray)
myArray = Array(mySet) // [2, 4, 60, 6, 15, 24, 1]
, Alors vous pouvez commander votre tableau comme vous voulez
myArray.sort{"151910920" < } // [1, 2, 4, 6, 15, 24, 60]
une autre solution Swift 3.0 Pour supprimer les doublons d'un tableau. Cette solution améliore de nombreuses autres solutions déjà proposées par:
- Préserver l'ordre des éléments dans le tableau d'entrée
- complexité Linéaire O(n): simple filtre passe de O(n) + set d'insertion O(1)
étant Donné le tableau entier:
let numberArray = [10, 1, 2, 3, 2, 1, 15, 4, 5, 6, 7, 3, 2, 12, 2, 5, 5, 6, 10, 7, 8, 3, 3, 45, 5, 15, 6, 7, 8, 7]
code fonctionnel:
func orderedSet<T: Hashable>(array: Array<T>) -> Array<T> {
var unique = Set<T>()
return array.filter { element in
return unique.insert(element).inserted
}
}
orderedSet(array: numberArray) // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
code D'extension de tableau:
extension Array where Element:Hashable {
var orderedSet: Array {
var unique = Set<Element>()
return filter { element in
return unique.insert(element).inserted
}
}
}
numberArray.orderedSet // [10, 1, 2, 3, 15, 4, 5, 6, 7, 12, 8, 45]
ce code tire profit du résultat retourné par l'opération insert
sur Set
, qui s'exécute sur O(1)
, et renvoie un tuple indiquant si l'article a été inséré ou s'il existait déjà dans l'ensemble.
si l'article était dans l'ensemble, filter
l'exclura du résultat final.
version syntaxique légèrement plus succincte de réponse Swift 2 de Daniel Krom , utilisant un nom de fermeture et d'argument en sténo, qui semble être basé sur réponse originale de Airspeed Velocity :
func uniq<S: SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {
var seen = [E: Bool]()
return source.filter { seen.updateValue(true, forKey: "151900920") == nil }
}
exemple de mise en œuvre d'un type personnalisé qui peut être utilisé avec uniq(_:)
(qui doit être conforme à Hashable
, et donc Equatable
, parce que Hashable
s'étend Equatable
):
func ==(lhs: SomeCustomType, rhs: SomeCustomType) -> Bool {
return lhs.id == rhs.id // && lhs.someOtherEquatableProperty == rhs.someOtherEquatableProperty
}
struct SomeCustomType {
let id: Int
// ...
}
extension SomeCustomType: Hashable {
var hashValue: Int {
return id
}
}
dans le code ci-dessus...
id
, tel qu'utilisé dans la surcharge de ==
, pourrait être n'importe quel type Equatable
(ou méthode qui retourne un type Equatable
, par exemple, someMethodThatReturnsAnEquatableType()
). Le code avec commentaires montre une extension du contrôle pour l'égalité, où someOtherEquatableProperty
est une autre propriété d'un type Equatable
(mais pourrait aussi être une méthode qui retourne un type Equatable
).
id
, comme utilisé dans le hashValue
biens calculés (requis pour se conformer à Hashable
), pourrait être tout Hashable
(et donc Equatable
) propriété (ou méthode qui retourne un Hashable
type).
exemple d'utilisation de uniq(_:)
:
var someCustomTypes = [SomeCustomType(id: 1), SomeCustomType(id: 2), SomeCustomType(id: 3), SomeCustomType(id: 1)]
print(someCustomTypes.count) // 4
someCustomTypes = uniq(someCustomTypes)
print(someCustomTypes.count) // 3
pour les tableaux dont les éléments ne sont ni Hachables ni comparables (par exemple, objets complexes, dictionnaires ou structures), cette extension fournit une façon généralisée d'éliminer les doublons:
extension Array
{
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
{
var uniqueKeys = Set<String>()
return filter{uniqueKeys.insert("\(keyValue("151900920"))").inserted}
}
}
// example usage: (for a unique combination of attributes):
peopleArray = peopleArray.filterDuplicate{ ("151900920".name, "151900920".age, "151900920".sex) }
vous n'avez pas à vous soucier de rendre les valeurs Hachables et il vous permet d'utiliser différentes combinaisons de champs pour l'unicité.
Vous pouvez toujours utiliser un Dictionnaire, parce qu'un Dictionnaire ne peut contenir que des valeurs uniques. Par exemple:
var arrayOfDates: NSArray = ["15/04/01","15/04/01","15/04/02","15/04/02","15/04/03","15/04/03","15/04/03"]
var datesOnlyDict = NSMutableDictionary()
var x = Int()
for (x=0;x<(arrayOfDates.count);x++) {
let date = arrayOfDates[x] as String
datesOnlyDict.setValue("foo", forKey: date)
}
let uniqueDatesArray: NSArray = datesOnlyDict.allKeys // uniqueDatesArray = ["15/04/01", "15/04/03", "15/04/02"]
println(uniqueDatesArray.count) // = 3
comme vous pouvez le voir, le tableau résultant ne sera pas toujours dans l'ordre. Si vous souhaitez trier/commander le tableau, ajoutez ceci:
var sortedArray = sorted(datesOnlyArray) {
(obj1, obj2) in
let p1 = obj1 as String
let p2 = obj2 as String
return p1 < p2
}
println(sortedArray) // = ["15/04/01", "15/04/02", "15/04/03"]
.
j'ai utilisé la réponse de @Jean-Philippe Pellet et fait une extension de réseau qui fait des opérations de type set-like sur les réseaux, tout en maintenant l'ordre des éléments.
/// Extensions for performing set-like operations on lists, maintaining order
extension Array where Element: Hashable {
func unique() -> [Element] {
var seen: [Element:Bool] = [:]
return self.filter({ seen.updateValue(true, forKey: "151900920") == nil })
}
func subtract(takeAway: [Element]) -> [Element] {
let set = Set(takeAway)
return self.filter({ !set.contains("151900920") })
}
func intersect(with: [Element]) -> [Element] {
let set = Set(with)
return self.filter({ set.contains("151900920") })
}
}
Permettez-moi de Suggérer une réponse similaire à réponse de Scott Gardner mais avec une syntaxe laconique Plus utilisant réduire. Cette solution supprime les doublons d'un tableau d'objets personnalisés (en gardant l'ordre initial)
// Custom Struct. Can be also class.
// Need to be `equitable` in order to use `contains` method below
struct CustomStruct : Equatable {
let name: String
let lastName : String
}
// conform to Equatable protocol. feel free to change the logic of "equality"
func ==(lhs: CustomStruct, rhs: CustomStruct) -> Bool {
return (lhs.name == rhs.name && lhs.lastName == rhs.lastName)
}
let categories = [CustomStruct(name: "name1", lastName: "lastName1"),
CustomStruct(name: "name2", lastName: "lastName1"),
CustomStruct(name: "name1", lastName: "lastName1")]
print(categories.count) // prints 3
// remove duplicates (and keep initial order of elements)
let uniq1 : [CustomStruct] = categories.reduce([]) { "151900920".contains() ? "151900920" : "151900920" + [] }
print(uniq1.count) // prints 2 - third element has removed
et juste si vous vous demandez comment cela fonctionne réduire magie-ici est exactement le même , mais en utilisant plus de réduire la syntaxe
let uniq2 : [CustomStruct] = categories.reduce([]) { (result, category) in
var newResult = result
if (newResult.contains(category)) {}
else {
newResult.append(category)
}
return newResult
}
uniq2.count // prints 2 - third element has removed
vous pouvez simplement copier-coller ce code dans un Swift Aire de jeux et de jouer.
C'est juste une implémentation très simple et pratique. Une propriété calculée dans une extension D'un tableau qui a des éléments égalisables.
extension Array where Element: Equatable {
/// Array containing only _unique_ elements.
var unique: [Element] {
var result: [Element] = []
for element in self {
if !result.contains(element) {
result.append(element)
}
}
return result
}
}
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
if !answer1.contains(i) {
answer1.append(i)
}}
return answer1
}
Utilisation:
let f = removeDublicate(ab: [1,2,2])
print(f)
Swift 4.2 Testé
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: [Iterator.Element: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: "151900920") == nil }
}
}
Voici la réponse que j'ai trouvée après avoir cherché sur le web et ne pas avoir trouvé ce que je cherchais. En utilisant un ensemble, vous pouvez ajouter tous les éléments avec réduire. Je prends alors le résultat et le convertit en un tableau trié.
let initialArray = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let distinct2 = initialArray.reduce(Set<Int>(), combine: { (set, current) -> Set<Int> in
var tmp = set
tmp.insert(current)
return tmp
})
// distinct2 is now a set containing {2, 4, 60, 6, 15, 24, 1}
// Make it into a sorted array
let sorted = Array(distinct2).sorted(<) // Returns [1, 2, 4, 6, 15, 24, 60]
ici j'ai fait un peu de O(n) solution pour les objets. Pas une solution de quelques lignes, mais...
struct DistinctWrapper <T>: Hashable {
var underlyingObject: T
var distinctAttribute: String
var hashValue: Int {
return distinctAttribute.hashValue
}
}
func distinct<S : SequenceType, T where S.Generator.Element == T>(source: S,
distinctAttribute: (T) -> String,
resolution: (T, T) -> T) -> [T] {
let wrappers: [DistinctWrapper<T>] = source.map({
return DistinctWrapper(underlyingObject: "151900920", distinctAttribute: distinctAttribute("151900920"))
})
var added = Set<DistinctWrapper<T>>()
for wrapper in wrappers {
if let indexOfExisting = added.indexOf(wrapper) {
let old = added[indexOfExisting]
let winner = resolution(old.underlyingObject, wrapper.underlyingObject)
added.insert(DistinctWrapper(underlyingObject: winner, distinctAttribute: distinctAttribute(winner)))
} else {
added.insert(wrapper)
}
}
return Array(added).map( { return "151900920".underlyingObject } )
}
func == <T>(lhs: DistinctWrapper<T>, rhs: DistinctWrapper<T>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
// tests
// case : perhaps we want to get distinct addressbook list which may contain duplicated contacts like Irma and Irma Burgess with same phone numbers
// solution : definitely we want to exclude Irma and keep Irma Burgess
class Person {
var name: String
var phoneNumber: String
init(_ name: String, _ phoneNumber: String) {
self.name = name
self.phoneNumber = phoneNumber
}
}
let persons: [Person] = [Person("Irma Burgess", "11-22-33"), Person("Lester Davidson", "44-66-22"), Person("Irma", "11-22-33")]
let distinctPersons = distinct(persons,
distinctAttribute: { (person: Person) -> String in
return person.phoneNumber
},
resolution:
{ (p1, p2) -> Person in
return p1.name.characters.count > p2.name.characters.count ? p1 : p2
}
)
// distinctPersons contains ("Irma Burgess", "11-22-33") and ("Lester Davidson", "44-66-22")
je crois qu'il serait bon d'offrir une fonction uniq()
et uniqInPlace()
pour muter un tableau en supprimant ses valeurs. Cela fonctionne de manière similaire aux fonctions sort()
et sortInPlace()
fournies par Swift. Aussi, puisque c'est un Tableau, il faut garder l'ordre d'origine des éléments.
extension Array where Element: Equatable {
public func uniq() -> [Element] {
var arrayCopy = self
arrayCopy.uniqInPlace()
return arrayCopy
}
mutating public func uniqInPlace() {
var seen = [Element]()
var index = 0
for element in self {
if seen.contains(element) {
removeAtIndex(index)
} else {
seen.append(element)
index++
}
}
}
}
vous ne pouvez utiliser uniqInPlace()
que sur un tableau de variables (i.e. var
) car vous ne pouvez pas muter un tableau constant (i.e. let
).
quelques exemples d'usage:
var numbers = [1, 6, 2, 2, 4, 1, 5]
numbers.uniqInPlace() // array is now [1, 6, 2, 4, 5]
let strings = ["Y", "Z", "A", "Y", "B", "Y", "Z"]
let uniqStrings = strings.uniq() // uniqStrings is now ["Y", "Z", "A", "B"]
Swift 3
basé sur la réponse de Jean-Philippe Pellet , j'ai mis à jour sa syntaxe pour Swift 3.
func uniq<S : Sequence, T : Hashable where S.Iterator.Element == T>(source: S) -> [T] {
var buffer = [T]()
var added = Set<T>()
for elem in source {
if !added.contains(elem) {
buffer.append(elem)
added.insert(elem)
}
}
return buffer
}
let vals = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let uniqueVals = uniq(source: vals) // [1, 4, 2, 6, 24, 15, 60]
dans Swift 3.0 la solution la plus simple et la plus rapide que j'ai trouvée pour éliminer les éléments dupliqués tout en gardant l'ordre:
extension Array where Element:Hashable {
var unique: [Element] {
var set = Set<Element>() //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(value) {
set.insert(value)
arrayOrdered.append(value)
}
}
return arrayOrdered
}
}
j'ai fait une extension simple-comme-possible à cette fin.
extension Array where Element: Equatable {
func containsHowMany(_ elem: Element) -> Int {
return reduce(0) { == elem ? "151900920" + 1 : "151900920" }
}
func duplicatesRemoved() -> Array {
return self.filter { self.containsHowMany("151900920") == 1 }
}
mutating func removeDuplicates() {
self = self.duplicatesRemoved(()
}
}
vous pouvez utiliser duplicatesRemoved()
pour obtenir un nouveau tableau, dont les éléments dupliqués sont supprimés, ou removeDuplicates()
pour muter lui-même. Voir:
let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
let noDuplicates = arr.duplicatesRemoved()
print(arr) // [1, 1, 1, 2, 2, 3, 4, 5, 6, 6, 6, 6, 6, 7, 8]
print(noDuplicates) // [1, 2, 3, 4, 5, 6, 7, 8]
arr.removeDuplicates()
print(arr) // [1, 2, 3, 4, 5, 6, 7, 8]
cela fonctionne aussi (Swift 4)
let sortedValues = Array(Set(array)).sorted()
Swift 4.x:
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
return Array(Set<Iterator.Element>(self))
}
func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { "151900920".contains() ? "151900920" : "151900920" + [] }
}
}
utilisation:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()
ou
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
préserve des valeurs uniques et préserve le tri dans un tableau.
(utilisant Swift 3)
var top3score: [Int] = []
outerLoop: for i in 0..<top10score.count {
dlog(message: String(top10score[i]))
if top3score.count == 3 {
break
}
for aTop3score in top3score {
if aTop3score == top10score[i] {
continue outerLoop
}
}
top3score.append(top10score[i])
}
print("top10score is \(top10score)") //[14, 5, 5, 5, 3, 3, 2, 2, 2, 2]
print("top3score is \(top3score)") //[14, 5, 3]