删除数组中的重复对象
我有一个包含我的Post
对象的数组。 每个Post
都有一个id
属性。
有没有一种更有效的方法来find我的数组中重复的邮政编码比
for post1 in posts { for post2 in posts { if post1.id == post2.id { posts.removeObject(post2) } } }
我将build议2个解决scheme。
这两种方法都需要Post
是可Hashable
和可平等的
使post符合可哈希和可衡量
这里我假设你的Post
结构(或者类)有一个String
types的id
属性。
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 }
解决scheme1(失去原来的订单)
要删除重复,你可以使用一个Set
let uniquePosts = Array(Set(posts))
解决scheme2(保存订单)
var alreadyThere = Set<Post>() let uniquePosts = posts.flatMap { (post) -> Post? in guard !alreadyThere.contains(post) else { return nil } alreadyThere.insert(post) return post }
(更新了Swift 3)
正如我在这个问题的评论中所提到的那样,您可以在我们之前标记此post的线程中使用经过修改的Daniel Kroms解决scheme。 只要让你的Post
对象可哈希(通过id
属性隐含地equatable),并实现一个修改(使用Set
而不是Dictionary
;在链接的方法中的字典值不被使用)Daniel Kroms uniq
函数的版本如下:
func uniq<S: Sequence, E: Hashable>(_ source: S) -> [E] where E == S.Iterator.Element { var seen = Set<E>() 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)] */
这将在保持原始数组顺序的同时删除重复项。
辅助函数uniq
作为Sequence
扩展
或者使用一个自由函数,我们可以实现uniq
作为受约束的Sequence
扩展:
extension Sequence where Iterator.Element: Hashable { func uniq() -> [Iterator.Element] { var seen = Set<Iterator.Element>() 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)] */
我的“纯”的Swift解决scheme没有后置符合Hashable(由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)]
我更喜欢(1)封装成函数(又名func unique(posts:[Post])->[Post]
),也许是一个扩展Array ….
使用Set
要使用它,使您的后可哈希和实现==
运算符
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}())!)]
用重复数组创build一个数组
let postsSet = Set(posts)
这是无序的,创build一个新的数组,应用顺序。
let uniquePosts = Array(postsSet).sort { (p1, p2) -> Bool in return p1.date.timeIntervalSince1970 < p2.date.timeIntervalSince1970 }
您可以使用包装类,而不是使您的Post
模型可哈希。 这个包装类将使用post对象属性来计算散列和相等性。
这个包装可以通过closuresconfiguration:
class HashableWrapper<T>: 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 ==<T>(lhs:HashableWrapper<T>, rhs:HashableWrapper<T>) -> Bool { return lhs.equal(obj1: lhs.object,obj2: rhs.object) }
邮政可以很简单
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 } }
让我们像以前一样创build一些post
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}())!) ]
现在我们用闭包为每个post创build包装对象,以确定相等和散列。 我们创build了这个集合。
let wrappers = posts.map { (p) -> HashableWrapper<Post> in return HashableWrapper<Post>(obj: p, equal: { (obj1, obj2) -> Bool in return obj1.id == obj2.id }, hash: { (obj) -> Int in return Int(obj.id) }) } let s = Set(wrappers)
现在我们提取包装的对象并按datesorting。
let objects = s.map { (w) -> Post in return w.object }.sort { (p1, p2) -> Bool in return p1.date.timeIntervalSince1970 > p2.date.timeIntervalSince1970 }
和
print(objects.map{$0.id})
版画
[1, 3, 2]
而不是使用可哈希对象,你可以使用一个集合。 取一个你想删除重复的属性值,并用它作为你的testing值。 在我的例子中,我正在检查重复的ISBN值。
do { try fetchRequestController.performFetch() print(fetchRequestController.fetchedObjects?.count) var set = Set<String>() 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() }
保持秩序,不增加额外的状态:
func removeDuplicates<T: Equatable>(accumulator: [T], element: T) -> [T] { return accumulator.contains(element) ? accumulator : accumulator + [element] } posts.reduce([], removeDuplicates)
在swift 3中请参考下面的代码:
let filterSet = NSSet(array: orignalArray as NSArray as! [NSObject]) let filterArray = filterSet.allObjects as NSArray //NSArray print("Filter Array:\(filterArray)")
Swift 3.1最优雅的解决scheme (Thanx dfri )
Apple Swift 3.1(swiftlang-802.0.51 clang-802.0.41)
func uniq<S: Sequence, E: Hashable>(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 } }
和额外的扩展
public extension Sequence { func distinct<E: Hashable>() -> [E] where E == Iterator.Element { return Array(Set(self)) } }