删除数组中的重复对象

时间:2016-01-10 18:15:52

标签: ios swift

我有一个包含Post个对象的数组。每个Post都有一个id属性。

是否有更有效的方法可以在我的数组中找到重复的帖子ID而不是

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

16 个答案:

答案 0 :(得分:26)

我将建议2个解决方案。

这两种方法都需要PostHashable和Equatable

使帖子符合Hashable和Equatable

这里我假设您的Post结构(或类)具有id类型的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
}

解决方案1(丢失原始订单)

要删除重复内容,您可以使用Set

let uniquePosts = Array(Set(posts))

解决方案2(保留订单)

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

答案 1 :(得分:3)

我的纯粹&#39;没有Post符合Hashable的Swift解决方案(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]),也许是扩展数组....

答案 2 :(得分:3)

(针对Swift 3更新)

正如我在对该问题的评论中提到的,您可以在the thread we previously marked this post to be duplicate of中使用修改后的Daniel Kroms解决方案。只需使Post对象可以缓存(通过id属性隐式等同)并实现修改(使用Set而不是Dictionary;不使用链接方法中的dict值无论如何)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)] */

这将删除重复项,同时保持原始数组的顺序。

辅助功能uniqSequence分机

除了使用免费功能外,我们还可以将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)] */

答案 3 :(得分:2)

保留顺序,不添加额外状态:

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

posts.reduce([], removeDuplicates)

答案 4 :(得分:2)

在swift 3中,请参阅以下代码:

onDestroy

答案 5 :(得分:1)

使用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}())!)]

从具有重复项的数组创建集

let postsSet = Set(posts)

这是无序的,创建一个新数组,应用订单。

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

您也可以使用包装类,而不是使Post模型可以使用。此包装类将使用post objects属性来计算哈希值和相等性 这个包装器可以通过闭包来配置:

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)
}

The Post可能只是

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
    }
}

让我们像以前一样创建一些帖子

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}())!)
]

现在,我们使用闭包为每个帖子创建包装器对象,以确定相等性和哈希值。我们创建了这个集合。

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)

现在我们提取包装的对象并按日期排序。

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]

答案 6 :(得分:1)

这也适用于多维数组:

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

答案 7 :(得分:1)

我在Swift 5上的解决方案:

添加扩展名:

public class MainActivity extends Activity {

private EditText e1;
private EditText e2;
private TextView tv4;


@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    e1 = (EditText) findViewById(R.id.et1);
    e2 = (EditText) findViewById(R.id.et2);
    tv4 = (TextView) findViewById(R.id.tv4);
}

Class Client,重要的是具有像Hashable这样的类:

extension Array where Element: Hashable {

    func removingDuplicates<T: Hashable>(byKey key: (Element) -> T)  -> [Element] {
         var result = [Element]()
         var seen = Set<T>()
         for value in self {
             if seen.insert(key(value)).inserted {
                 result.append(value)
             }
         }
         return result
     }

}

使用:

struct Client:Hashable {

   let uid :String
   let notifications:Bool

   init(uid:String,dictionary:[String:Any]) {
       self.uid = uid
       self.notifications = dictionary["notificationsStatus"] as? Bool ?? false
   }

   static func == (lhs: Client, rhs: Client) -> Bool {
    return lhs.uid == rhs.uid
   }

}

有个美好的一天,快恋人♥️

答案 8 :(得分:1)

func removeDuplicateElements(post: [Post]) -> [Post] {
    var uniquePosts = [Post]()
    for post in posts {
        if !uniquePosts.contains(where: {$0.postId == post.postId }) {
            uniquePosts.append(post)
        }
    }
    return uniquePosts
}

答案 9 :(得分:0)

您可以只使用一个集合,而不是使用可散列对象。获取要删除重复项的属性值,并将其用作测试值。在我的示例中,我正在检查重复的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()
}

答案 10 :(得分:0)

Swift 3.1最优雅的解决方案(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)]*/

答案 11 :(得分:0)

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))
    }
}

答案 12 :(得分:0)

post

中有一个很好的例子

这是一个数组扩展,用于基于给定的键返回对象的唯一列表:

extension Array {
    func unique<T:Hashable>(map: ((Element) -> (T)))  -> [Element] {
        var set = Set<T>() //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(map(value)) {
                set.insert(map(value))
                arrayOrdered.append(value)
            }
        }

        return arrayOrdered
    }
}

以您的示例为例:

let uniquePosts = posts.unique{$0.id ?? ""}

答案 13 :(得分:0)

这是一个比这里更流行的变体更不专门的问题:https://stackoverflow.com/a/33553374/652038

使用我的答案,您可以这样做:

posts.firstUniqueElements(\.id)

答案 14 :(得分:0)

保留原始顺序的通用解决方案是:

extension Array {
    func unique(selector:(Element,Element)->Bool) -> Array<Element> {
        return reduce(Array<Element>()){
            if let last = $0.last {
                return selector(last,$1) ? $0 : $0 + [$1]
            } else {
                return [$1]
            }
        }
    }
}

let uniquePosts = posts.unique{$0.id == $1.id }

答案 15 :(得分:-1)

我的解决方法是:

for i in 0...modelList.count - 1 {
  var temp = modelList.count
  for j in stride(from: i + 1, to: temp - 1, by: 1) {
    if modelList[i].id == modelList[j].id {
      modelList.remove(at: i)
      temp -= 1
    }
  }
}