Je pourrais avoir un tableau qui ressemble à ce qui suit:
[1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
Notez que les doublons de 2, 6 et 15 ont été supprimés pour garantir qu’il n’y avait qu’un élément de chaque élément identique. Swift fournit-il un moyen de le faire facilement ou devrai-je le faire moi-même?
Vous pouvez rouler le vôtre, par exemple comme ceci ( mis à jour pour Swift 1.2 avec Set ):
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 facilement convertir en un ensemble et revenir à un tableau:
let unique = Array(Set(originals))
Cela ne garantit pas le maintien de l'ordre d'origine du tableau.
Beaucoup de réponses disponibles ici, mais j'ai raté cette extension simple, adaptée à 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
}
}
C'est très simple. Peut être appelé comme ça:
let arrayOfInts = [2, 2, 4, 4]
print(arrayOfInts.removeDuplicates()) // Prints: [2, 4]
Filtrage basé sur les propriétés
Pour filtrer un tableau en fonction de 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: $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
Que vous pouvez appeler comme suit:
let filteredElements = myElements.filterDuplicates { $0.PropertyOne == $1.PropertyOne && $0.PropertyTwo == $1.PropertyTwo }
Cela prend quelques-unes des bonnes informations déjà sur cette page, et applique l'approche si possible Hashable/Set, et revient au code Equatable sinon.
Swift 4 change pour l'extension Equatable
(Hashable
reste identique)
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))
Contraindre les éléments de la collection à Equatable, vous pouvez utiliser contient:
extension Collection where Element: Equatable {
var orderedSet: [Element] {
var array: [Element] = []
return compactMap {
if array.contains($0) {
return nil
} else {
array.append($0)
return $0
}
}
}
}
Une autre option consiste à contraindre l'élément de collection à Hashable et à utiliser un ensemble pour contrôler les éléments à mapper dans le résultat:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return compactMap { set.insert($0).inserted ? $0 : nil }
}
}
en utilisant un filtre:
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
}
ou en utilisant NSOrderedSet:
extension Array where Element: Hashable {
var orderedSet: Array {
return NSOrderedSet(array: self).array as? Array ?? []
}
}
En utilisant Swift 4 réduire (en :)
extension Collection where Element: Hashable {
var orderedSet: [Element] {
var set: Set<Element> = []
return reduce(into: []) { set.insert($1).inserted ? $0.append($1) : () }
}
}
let integers = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
let integersOrderedSet = integers.orderedSet // [1, 4, 2, 6, 24, 15, 60]
Vous pouvez également étendre le protocole RangeReplaceableCollection
pour lui permettre d'être également utilisé avec les types StringProtocol (Strings et SubStrings):
extension RangeReplaceableCollection where Element: Hashable {
var orderedSet: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}
mutating func removeDuplicates() {
var set = Set<Element>()
removeAll { !set.insert($0).inserted }
}
}
"abcdefabcghi".orderedSet // "abcdefghi"
"abcdefabcghi".dropFirst(3).orderedSet // "defabcghi"
Méthode de mutation
var string = "abcdefabcghi"
string.removeDuplicates()
string // "abcdefghi"
var substring = "abcdefabcdefghi".dropFirst(3) // "defabcdefghi"
substring.removeDuplicates()
substring // "defabcghi"
Swift 4
Garanti pour continuer à commander.
extension Array where Element: Equatable {
func removingDuplicates() -> Array {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
Swift 4
public extension Array where Element: Hashable {
func uniqued() -> [Element] {
var seen = Set<Element>()
return filter{ seen.insert($0).inserted }
}
}
chaque tentative de insert
renverra également un Tuple: (inserted: Bool, memberAfterInsert: Set.Element)
. Voir documentation .
L'utilisation de la valeur renvoyée nous évite d'avoir à boucler ou à effectuer une 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
}
Inclus pour opposer l'approche impérative de Jean-Pillippe à une approche fonctionnelle.
En prime, cette fonction fonctionne aussi bien avec des chaînes que des tableaux!
Edit: Cette réponse a été écrite en 2014 pour Swift 1.0 (avant que Set
soit disponible dans Swift). Il ne nécessite pas de conformité Hashable et s'exécute en temps quadratique.
avecuniqfunction 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
})
}
utilisation:
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 conserve l'ordre d'origine du tableau, mais utilise un Set
pour effectuer les recherches contains
afin d'éviter le coût O(n)
avec la méthode contains(_:)
du tableau.
public extension Array where Element: Hashable {
/// Return the array with all duplicates removed.
///
/// i.e. `[ 1, 2, 3, 1, 2 ].uniqued() == [ 1, 2, 3 ]`
///
/// - note: Taken from stackoverflow.com/a/46354989/3141234, as
/// per @Alexander's comment.
public func uniqued() -> [Element] {
var seen = Set<Element>()
return self.filter { seen.insert($0).inserted }
}
}
ou si vous n'avez pas Hashable, vous pouvez faire ceci:
public extension Sequence where Iterator.Element: Equatable {
public func uniqued() -> [Iterator.Element] {
var buffer: [Iterator.Element] = []
for element in self {
guard !buffer.contains(element) else { continue }
buffer.append(element)
}
return buffer
}
}
Vous pouvez les coller dans votre application. Swift choisira la bonne en fonction du type Iterator.Element
de votre séquence.
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]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}
usage:
["Ljubljana", "London", "Los Angeles", "Ljubljana"].unique()
ou
["Ljubljana", "London", "Los Angeles", "Ljubljana"].uniqueOrdered()
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:
É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 du 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 parti du résultat renvoyé par l'opération insert
sur Set
qui s'exécute sur O(1)
et renvoie un tuple indiquant si l'élément a été inséré ou s'il existait déjà dans l'ensemble.
Si l'élément était dans l'ensemble, filter
l'exclura du résultat final.
Vous pouvez utiliser directement une collection de jeux pour supprimer les doublons, puis les reconvertir en 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]
Ensuite, vous pouvez commander votre tableau comme vous le souhaitez
myArray.sort{$0 < $1} // [1, 2, 4, 6, 15, 24, 60]
Inspiré par https://www.swiftbysundell.com/posts/the-power-of-key-paths-in-Swift , nous pouvons déclarer un outil plus puissant qui peut filtrer l’unicité de n’importe quel chemin keyPath. Grâce aux commentaires d'Alexander sur diverses réponses concernant la complexité, les solutions ci-dessous devraient être quasi optimales.
Nous étendons avec une fonction qui est capable de filtrer l'unicité sur n'importe quel keyPath:
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 }
}
}
Si nous voulons l’unicité pour les éléments eux-mêmes, comme dans la question, nous utilisons 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] */
Si nous voulons l’unicité pour autre chose (comme pour la id
d’une collection d’objets), nous utilisons le chemin de clé de notre choix:
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}] */
Nous étendons avec une fonction de mutation capable de filtrer l’unicité sur n’importe quel chemin 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 }
}
}
Si nous voulons l’unicité pour les éléments eux-mêmes, comme dans la question, nous utilisons 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] */
Si nous voulons l’unicité pour autre chose (comme pour la id
d’une collection d’objets), nous utilisons le chemin de clé de notre choix:
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}] */
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: [Iterator.Element: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: $0) == nil }
}
}
Version syntaxique légèrement plus succincte de Daniel Krom's Swift 2 answer , utilisant un nom de clôture et d'argument, qui semble reposer sur la 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: $0) == nil }
}
Exemple d'implémentation d'un type personnalisé pouvant être utilisé avec uniq(_:)
(qui doit être conforme à Hashable
, et donc Equatable
, car 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 ==
, peut être n’importe quel type Equatable
(ou une méthode renvoyant un type Equatable
, par exemple, someMethodThatReturnsAnEquatableType()
). Le code de commentaire commente l'extension du contrôle d'égalité, où someOtherEquatableProperty
est une autre propriété d'un type Equatable
(mais peut également être une méthode renvoyant un type Equatable
).
id
, telle qu'utilisée dans la propriété calculée hashValue
(requise pour se conformer à Hashable
), peut être toute propriété Hashable
(et donc Equatable
) (ou méthode renvoyant un type Hashable
).
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 où les éléments ne sont ni Hashable ni Comparable (par exemple, objets complexes, dictionnaires ou structures), cette extension fournit un moyen généralisé de supprimer les doublons:
extension Array
{
func filterDuplicate<T>(_ keyValue:(Element)->T) -> [Element]
{
var uniqueKeys = Set<String>()
return filter{uniqueKeys.insert("\(keyValue($0))").inserted}
}
}
// example usage: (for a unique combination of attributes):
peopleArray = peopleArray.filterDuplicate{ ($0.name, $0.age, $0.sex) }
Vous n'avez pas à vous soucier de rendre les valeurs Hashable et cela vous permet d'utiliser différentes combinaisons de champs pour plus d'unicité.
func removeDublicate (ab: [Int]) -> [Int] {
var answer1:[Int] = []
for i in ab {
if !answer1.contains(i) {
answer1.append(i)
}}
return answer1
}
Usage:
let f = removeDublicate(ab: [1,2,2])
print(f)
ici, j'ai fait quelques O(n) solutions 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: $0, distinctAttribute: distinctAttribute($0))
})
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 $0.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")
Vous pouvez toujours utiliser un dictionnaire, car 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 constater, le tableau résultant ne sera pas toujours "en ordre". Si vous souhaitez trier/ordonner 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"]
.
Permettez-moi de suggérer une réponse semblable à celle de Réponse de Scott Gardner mais avec une syntaxe plus laconique en utilisant reduction . Cette solution supprime les doublons d'un tableau d'objets personnalisés (en conservant 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([]) { $0.contains($1) ? $0 : $0 + [$1] }
print(uniq1.count) // prints 2 - third element has removed
Et juste si vous vous demandez comment cela fonctionne: la magie de réduction est la même, mais la syntaxe de réduction est plus étendue.
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 une aire de jeu Swift et jouer.
J'ai utilisé la réponse de @ Jean-Philippe Pellet pour créer une extension Array qui effectue des opérations sur les tableaux, tout en conservant 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: $0) == nil })
}
func subtract(takeAway: [Element]) -> [Element] {
let set = Set(takeAway)
return self.filter({ !set.contains($0) })
}
func intersect(with: [Element]) -> [Element] {
let set = Set(with)
return self.filter({ set.contains($0) })
}
}
Terminé....
Exemple
let array = [1,1,1,1,2,2,2,2,4,6,8]
let orderedSet : NSOrderedSet = NSOrderedSet(array: array)
let arrayWithoutDuplicates : NSArray = orderedSet.array as NSArray
sortie de arrayWithoutDuplicates - [1,2,4,6,8]
Je pense qu’il serait bon d’offrir une fonction uniq()
et uniqInPlace()
pour muter un tableau en supprimant ses valeurs. Cela fonctionne comme les fonctions sort()
et sortInPlace()
fournies par Swift. De plus, puisqu'il s'agit d'un tableau, il devrait conserver 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 que uniqInPlace()
sur un tableau Array variable (c.-à-d. var
), car vous ne pouvez pas muter un tableau constant (c.-à-d. let
).
Quelques exemples d'utilisation:
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"]
Ceci est juste une implémentation très simple et pratique. Une propriété calculée dans une extension d'un tableau contenant des éléments équivalents.
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
}
}
c’est le moyen le plus simple dans Swift 4.2 et suivant le code ci-dessous
let keyarray:NSMutableArray = NSMutableArray()
for object in dataArr
{
if !keysArray.contains(object){
keysArray.add(object)
}
}
print(keysArray)
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 respectant 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 aussi simple que possible à cette fin.
extension Array where Element: Equatable {
func containsHowMany(_ elem: Element) -> Int {
return reduce(0) { $1 == elem ? $0 + 1 : $0 }
}
func duplicatesRemoved() -> Array {
return self.filter { self.containsHowMany($0) == 1 }
}
mutating func removeDuplicates() {
self = self.duplicatesRemoved(()
}
}
Vous pouvez utiliser duplicatesRemoved()
pour obtenir un nouveau tableau, dont les éléments en double sont supprimés, ou removeDuplicates()
pour se muter. 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]
Le moyen le plus simple serait d'utiliser NSOrderedSet, qui stocke des éléments uniques et préserve l'ordre des éléments. Comme:
func removeDuplicates(from items: [Int]) -> [Int] {
let uniqueItems = NSOrderedSet(array: items)
return (uniqueItems.array as? [Int]) ?? []
}
let arr = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
removeDuplicates(from: arr)
Préservez les valeurs uniques et préservez le tri dans un tableau.
(en 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]
Pensez comme un programmeur fonctionnel :)
Pour filtrer la liste en fonction de la présence ou non de l'élément, vous avez besoin de l'index. Vous pouvez utiliser enumerated
pour obtenir l'index et map
pour revenir à la liste de valeurs.
let unique = myArray
.enumerated()
.filter{ myArray.firstIndex(of: $0.1) == $0.0 }
.map{ $0.1 }
Cela garantit la commande. Si l'ordre ne vous dérange pas, la réponse existante de Array(Set(myArray))
est plus simple et probablement plus efficace.
Ma solution, il semble que cela puisse être en O(n), car l'accès à la carte de hachage est O (1) et le filtre est O (n). Il utilise également par fermeture pour sélectionner la propriété permettant de distinguer les éléments les uns après les autres.
extension Sequence {
func distinct<T: Hashable>(by: (Element) -> T) -> [Element] {
var seen: [T: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: by($0)) == nil }
}
}
Voici un moyen plus flexible de rendre une séquence unique avec une fonction de correspondance personnalisée.
extension Sequence where Iterator.Element: Hashable {
func unique(matching: (Iterator.Element, Iterator.Element) -> Bool) -> [Iterator.Element] {
var uniqueArray: [Iterator.Element] = []
forEach { element in
let isUnique = uniqueArray.reduce(true, { (result, item) -> Bool in
return result && matching(element, item)
})
if isUnique {
uniqueArray.append(element)
}
}
return uniqueArray
}
}
func removeDuplicates(_ nums: inout [Int]) -> Int {
nums = Set(nums).sorted()
return nums.count
}
Exemple
var arr = [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9]
removeDuplicates(&arr)
print(arr) // [1,2,3,4,5,6,7,8,9]
Cela fonctionne dans Swift 4, si vous ne voulez pas/n'avez pas besoin de convertir le résultat en tableau, mais pouvez le faire avec un ensemble. Le résultat n'est pas trié par défaut, mais vous pouvez le faire avec trié (), qui renvoie un tableau, comme indiqué dans l'instruction print.
let array = [1, 4, 2, 2, 6, 24, 15, 2, 60, 15, 6]
var result = Set<Int>()
_ = array.map{ result.insert($0) }
print(result.sorted()) // [1, 2, 4, 6, 15, 24, 60]
J'ai créé une fonction d'ordre supérieur dont la complexité temporelle est o (n). En outre, une fonctionnalité telle que la carte permet de renvoyer le type de votre choix.
extension Sequence {
func distinct<T,U>(_ provider: (Element) -> (U, T)) -> [T] where U: Hashable {
var uniqueKeys = Set<U>()
var distintValues = [T]()
for object in self {
let transformed = provider(object)
if !uniqueKeys.contains(transformed.0) {
distintValues.append(transformed.1)
uniqueKeys.insert(transformed.0)
}
}
return distintValues
}
}