Eliminar objetos duplicados en una matriz

Tengo una matriz que contiene mis objetos Post . Cada Post tiene una propiedad de id .

¿Hay una forma más efectiva de encontrar ID de publicación duplicadas en mi matriz que

 for post1 in posts { for post2 in posts { if post1.id == post2.id { posts.removeObject(post2) } } } 

Voy a sugerir 2 soluciones.

Ambos enfoques necesitarán que Post sea Hashable y Equatable

Hacer que los postes se ajusten a Hashable y Equatable

Aquí estoy suponiendo que su estructura de Post (o clase) tiene una propiedad de id . De tipo String .

 struct Post: Hashable, Equatable { let id: String var hashValue: Int { get { return id.hashValue } } } func ==(left:Post, right:Post) -> Bool { return left.id == right.id } 

Solución 1 (perdiendo el orden original)

Para eliminar duplicados, puede usar un Set

 let uniquePosts = Array(Set(posts)) 

Solución 2 (preservando la orden)

 var alreadyThere = Set() let uniquePosts = posts.flatMap { (post) -> Post? in guard !alreadyThere.contains(post) else { return nil } alreadyThere.insert(post) return post } 

(Actualizado para Swift 3)

Como mencioné en mi comentario a la pregunta, puede utilizar una solución modificada de Daniel Kroms en el hilo que previamente hemos marcado como duplicado de esta publicación . Simplemente haga que su objeto Post hashable (implícitamente equivalente mediante la propiedad id ) e implemente una versión modificada (utilizando Set vez de Dictionary ; el valor dict en el método vinculado no se usa de todos modos) de la función uniq de Daniel Kroms de la siguiente manera:

 func uniq(_ source: S) -> [E] where E == S.Iterator.Element { var seen = Set() return source.filter { seen.update(with: $0) == nil } } struct Post : Hashable { var id : Int var hashValue : Int { return self.id } } func == (lhs: Post, rhs: Post) -> Bool { return lhs.id == rhs.id } var posts : [Post] = [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] print(Posts) /* [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] */ var myUniquePosts = uniq(posts) print(myUniquePosts) /* [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 3), Post(id: 5), Post(id: 9)] */ 

Esto eliminará los duplicados mientras se mantiene el orden de la matriz original.


Helper function uniq como una extensión de Sequence

Alternativamente a usar una función libre, podríamos implementar uniq como una extensión de Sequence restringida:

 extension Sequence where Iterator.Element: Hashable { func uniq() -> [Iterator.Element] { var seen = Set() return filter { seen.update(with: $0) == nil } } } struct Post : Hashable { var id : Int var hashValue : Int { return self.id } } func == (lhs: Post, rhs: Post) -> Bool { return lhs.id == rhs.id } var posts : [Post] = [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] print(posts) /* [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] */ var myUniquePosts = posts.uniq() print(myUniquePosts) /* [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 3), Post(id: 5), Post(id: 9)] */ 

mis soluciones Swift ‘puras’ sin conformidad posterior a Hashable (requerido por Set)

 struct Post { var id: Int } let posts = [Post(id: 1),Post(id: 2),Post(id: 1),Post(id: 3),Post(id: 4),Post(id: 2)] // (1) var res:[Post] = [] posts.forEach { (p) -> () in if !res.contains ({ $0.id == p.id }) { res.append(p) } } print(res) // [Post(id: 1), Post(id: 2), Post(id: 3), Post(id: 4)] // (2) let res2 = posts.reduce([]) { (var r, p) -> [Post] in if !r.contains ({ $0.id == p.id }) { r.append(p) } return r } print(res2) // [Post(id: 1), Post(id: 2), Post(id: 3), Post(id: 4)] 

Prefiero (1) encapsulado en función (aka func unique(posts:[Post])->[Post] ), tal vez una extensión Array ….

Conservando el orden, sin agregar estado adicional:

 func removeDuplicates(accumulator: [T], element: T) -> [T] { return accumulator.contains(element) ? accumulator : accumulator + [element] } posts.reduce([], removeDuplicates) 

En swift 3, refiérase al siguiente código:

 let filterSet = NSSet(array: orignalArray as NSArray as! [NSObject]) let filterArray = filterSet.allObjects as NSArray //NSArray print("Filter Array:\(filterArray)") 

usa un conjunto

Para usarlo, haz que tu publicación sea accesible e implementa el operador ==

 import Foundation class Post: Hashable, Equatable { let id:UInt let title:String let date:NSDate var hashValue: Int { get{ return Int(self.id) } } init(id:UInt, title:String, date:NSDate){ self.id = id self.title = title self.date = date } } func ==(lhs: Post, rhs: Post) -> Bool { return lhs.id == rhs.id } let posts = [Post(id: 11, title: "sadf", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 1; c.year = 2016; return c}())!), Post(id: 33, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 3; c.month = 1; c.year = 2016; return c}())!), Post(id: 22, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 12; c.year = 2015; return c}())!), Post(id: 22, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 12; c.year = 2015; return c}())!)] 

Crear conjunto desde array con duplicados

 let postsSet = Set(posts) 

Esto no está ordenado, crea una nueva matriz, aplica orden.

 let uniquePosts = Array(postsSet).sort { (p1, p2) -> Bool in return p1.date.timeIntervalSince1970 < p2.date.timeIntervalSince1970 } 

En lugar de hacer que tu modelo de publicación sea hashable, también puedes usar una clase contenedora. Esta clase contenedora usaría la propiedad publicar objetos para calcular el hash y la igualdad.
esta envoltura podría ser configurable a través del cierre:

 class HashableWrapper: Hashable { let object: T let equal: (obj1: T,obj2: T) -> Bool let hash: (obj: T) -> Int var hashValue:Int { get { return self.hash(obj: self.object) } } init(obj: T, equal:(obj1: T, obj2: T) -> Bool, hash: (obj: T) -> Int) { self.object = obj self.equal = equal self.hash = hash } } func ==(lhs:HashableWrapper, rhs:HashableWrapper) -> Bool { return lhs.equal(obj1: lhs.object,obj2: rhs.object) } 

El mensaje podría ser simplemente

 class Post { let id:UInt let title:String let date:NSDate init(id:UInt, title:String, date:NSDate){ self.id = id self.title = title self.date = date } } 

Vamos a crear una publicación como antes

 let posts = [ Post(id: 3, title: "sadf", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 1; c.year = 2016; return c}())!), Post(id: 1, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 3; c.month = 1; c.year = 2016; return c}())!), Post(id: 2, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 12; c.year = 2015; return c}())!), Post(id: 2, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 1; c.month = 12; c.year = 2015; return c}())!), Post(id: 1, title: "sdfr", date: NSCalendar.currentCalendar().dateFromComponents({let c = NSDateComponents(); c.day = 3; c.month = 1; c.year = 2016; return c}())!) ] 

Ahora creamos objetos de envoltura para cada publicación con cierre para determinar la igualdad y el hash. Y creamos el conjunto.

 let wrappers = posts.map { (p) -> HashableWrapper in return HashableWrapper(obj: p, equal: { (obj1, obj2) -> Bool in return obj1.id == obj2.id }, hash: { (obj) -> Int in return Int(obj.id) }) } let s = Set(wrappers) 

Ahora extraemos los objetos envueltos y los clasificamos por fecha.

 let objects = s.map { (w) -> Post in return w.object }.sort { (p1, p2) -> Bool in return p1.date.timeIntervalSince1970 > p2.date.timeIntervalSince1970 } 

y

 print(objects.map{$0.id}) 

huellas dactilares

 [1, 3, 2] 

En lugar de usar un objeto que se puede cargar, simplemente podría usar un conjunto. Tome un valor de atributo para el que desee eliminar duplicados y utilícelo como valor de prueba. En mi ejemplo, estoy buscando valores duplicados de ISBN.

 do { try fetchRequestController.performFetch() print(fetchRequestController.fetchedObjects?.count) var set = Set() for entry in fetchRequestController.fetchedObjects! { if set.contains(entry.isbn!){ fetchRequestController.managedObjectContext.delete(entry) }else { set.insert(entry.isbn!) } } try fetchRequestController.performFetch() print(fetchRequestController.fetchedObjects?.count) } catch { fatalError() } 

Swift 3.1 La solución más elegante ( Thanf dfri )

Apple Swift versión 3.1 (swiftlang-802.0.51 clang-802.0.41)

 func uniq(source: S) -> [E] where E==S.Iterator.Element { var seen: [E:Bool] = [:] return source.filter({ (v) -> Bool in return seen.updateValue(true, forKey: v) == nil }) } struct Post : Hashable { var id : Int var hashValue : Int { return self.id } } func == (lhs: Post, rhs: Post) -> Bool { return lhs.id == rhs.id } var Posts : [Post] = [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] print(Posts) /* [Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 1), Post(id: 3), Post(id: 5), Post(id: 7), Post(id: 9)] */ var myUniquePosts = uniq(source: Posts) print(myUniquePosts) /*[Post(id: 1), Post(id: 7), Post(id: 2), Post(id: 3), Post(id: 5), Post(id: 9)]*/ 
 struct Post { var id: Int } extension Post: Hashable { var hashValue: Int { return id } static func == (lhs: Post, rhs: Post) -> Bool { return lhs.id == rhs.id } } 

y extensión adicional

 public extension Sequence { func distinct() -> [E] where E == Iterator.Element { return Array(Set(self)) } } 

Esto también funciona para arreglos multidimensionales:

 for (index, element) in arr.enumerated().reversed() { if arr.filter({ $0 == element}).count > 1 { arr.remove(at: index) } }