Удаление повторяющихся объектов в массиве
У меня есть массив, содержащий мои объекты Post
. Каждый Post
имеет свойство id
.
Есть ли более эффективный способ найти дубликат Post ID в моем массиве, чем
for post1 in posts {
for post2 in posts {
if post1.id == post2.id {
posts.removeObject(post2)
}
}
}
Ответы
Ответ 1
Я собираюсь предложить 2 решения.
Оба подхода потребуют Post
быть Hashable
и Equatable
Создание сообщения соответствует Hashable и Equableable
Здесь я предполагаю, что ваш Post
struct (или класс) имеет свойство 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
}
Ответ 2
мои "чистые" решения Swift без соответствия Post для 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) инкапсулироваться в функцию (aka func unique(posts:[Post])->[Post]
), возможно, массив расширений....
Ответ 3
(Обновлено для Swift 3)
Как я уже упоминал в своем комментарии к вопросу, вы можете использовать модифицированное решение Daniel Kroms в потоке, который мы ранее отмечали, чтобы это сообщение дублировалось. Просто сделайте свой объект Post
hashable (неявно равнозначным с помощью свойства 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)] */
Это приведет к удалению дубликатов при сохранении порядка исходного массива.
Вспомогательная функция 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)] */
Ответ 4
Сохранение порядка, без добавления дополнительного состояния:
func removeDuplicates<T: Equatable>(accumulator: [T], element: T) -> [T] {
return accumulator.contains(element) ?
accumulator :
accumulator + [element]
}
posts.reduce([], removeDuplicates)
Ответ 5
В быстром 3 см. ниже код:
let filterSet = NSSet(array: orignalArray as NSArray as! [NSObject])
let filterArray = filterSet.allObjects as NSArray //NSArray
print("Filter Array:\(filterArray)")
Ответ 6
используйте Установить
Чтобы использовать его, сделайте свой пост хешируемой и реализуйте оператор ==
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)
}
Сообщение может быть просто
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]
Ответ 7
Это также работает для многомерных массивов:
for (index, element) in arr.enumerated().reversed() {
if arr.filter({ $0 == element}).count > 1 {
arr.remove(at: index)
}
}
Ответ 8
Мое решение на Swift 5:
Добавить расширение:
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
}
}
Class Client, важно иметь такой класс, как Hashable:
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
}
}
Использование:
arrayClients.removingDuplicates(byKey: { $0.uid })
Удачного дня любителям быстрой ♥ ️
Ответ 9
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
}
Ответ 10
Вместо использования объекта hashable вы можете просто использовать набор. Возьмите значение атрибута, для которого вы хотите удалить дубликаты, и используйте это как тестовое значение. В моем примере я проверяю дубликаты значений 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()
}
Ответ 11
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)]*/
Ответ 12
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))
}
}
Ответ 13
Вот хороший пример из этого поста
Вот расширение Array, которое возвращает уникальный список объектов на основе заданного ключа:
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 ?? ""}
Ответ 14
мое решение:
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
}
}
}