Swift: удалить дубликаты из многомерного массива [duplicate]

Вот моя собственная реализация синглетонов. Все, что вам нужно сделать, это украсить класс; для получения синглтона вы должны использовать метод Instance. Вот пример:

   @Singleton
   class Foo:
       def __init__(self):
           print 'Foo created'

   f = Foo() # Error, this isn't how you get the instance of a singleton

   f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
   g = Foo.Instance() # Returns already created instance

   print f is g # True

И вот код:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)
6
задан Oscar Apeland 10 January 2016 в 19:15
поделиться

10 ответов

Я собираюсь предложить два решения.

Оба подхода потребуют Post, чтобы быть Hashable и Equatable

Создание сообщения соответствует Hashable и Equatable

Здесь я предполагаю, что ваш 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
}
21
ответ дан Luca Angeletti 21 August 2018 в 16:43
поделиться
  • 1
    Спасибо за ваш ответ. К сожалению, порядок имеет значение для меня, поскольку я хочу, чтобы посты показывались в хронологическом порядке. – Oscar Apeland 10 January 2016 в 19:27
  • 2
    @OscarApeland: Нет проблем, я добавил еще один раздел (решение 2), где сохранился первоначальный порядок. – Luca Angeletti 10 January 2016 в 19:29
  • 3
    при создании Set, внутренне проверяйте, содержит ли он (сообщение). чем вы делаете то же самое снова. Я не вижу никакого преимущества такого подхода. Несмотря на то, что мой собственный ответ был проголосован, соответствие Hashable и создание набора не является необходимым и, наконец, менее эффективным. пожалуйста, проверьте мой ответ ... – user3441734 12 January 2016 в 14:44
  • 4
    @ user3441734: Я не ответил на ваш вопрос. Я думаю, вы говорите о моем Solution 2. alreadyThere.contains в моем коде является обязательным, потому что мне нужно знать, присутствует ли post, который я оцениваю, в массиве, который я создаю и возвращаю. Вы не можете удалить эту часть. если вам нужны уникальные значения. – Luca Angeletti 12 January 2016 в 14:57
  • 5
    Я кодировал в Swift почти два года, и я понимаю только Hashable и Equatable с вашим объяснением :) Я думал, что это суперфункция, которую я не смогу понять. благодаря – J. Goce 25 January 2018 в 12:32

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)]*/
0
ответ дан Community 21 August 2018 в 16:43
поделиться
  • 1
    Подумайте о добавлении комментария к исходного ответа в следующий раз (запрос на обновление Swift X.X) before , чтобы копировать и повторно отправлять ответ только с незначительным обновлением. В случае, если автор оригинального ответа не отвечает на запрос об обновлении (и у вас недостаточно прав для обновления ответа самостоятельно), в то время было бы целесообразно дублировать ответ с обновлением для более новой языковой версии. Поскольку этот Q & amp; A в настоящее время стоит, у нас теперь есть два точно дублированных ответа, так как теперь я обновил свой первоначальный ответ на Swift 3. – dfri 15 April 2017 в 09:30

(Обновлено для 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 extension

В качестве альтернативы использованию свободной функции мы могли бы реализовать 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
ответ дан dfri 21 August 2018 в 16:43
поделиться

Вместо использования объекта 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()
}
0
ответ дан Gerard Taylor 21 August 2018 в 16:43
поделиться
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))
    }
}
0
ответ дан GSerjo 21 August 2018 в 16:43
поделиться

Это также работает для многомерных массивов:

for (index, element) in arr.enumerated().reversed() {
    if arr.filter({ $0 == element}).count > 1 {
        arr.remove(at: index)
    }
}
0
ответ дан IKavanagh 21 August 2018 в 16:43
поделиться

Сохранение порядка, без добавления дополнительного состояния:

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

posts.reduce([], removeDuplicates)
2
ответ дан mbdavis 21 August 2018 в 16:43
поделиться

В swift 3 см. ниже код:

let filterSet = NSSet(array: orignalArray as NSArray as! [NSObject])
let filterArray = filterSet.allObjects as NSArray  //NSArray
 print("Filter Array:\(filterArray)")
2
ответ дан Richard Telford 21 August 2018 в 16:43
поделиться

мои «чистые» решения 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]), возможно, расширение Array .. ..

2
ответ дан user3441734 21 August 2018 в 16:43
поделиться

используйте 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}())!)]

Создайте набор из массива с помощью duplates

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

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]
1
ответ дан vikingosegundo 21 August 2018 в 16:43
поделиться
  • 1
    Спасибо за огромные усилия. Я решил принять другой ответ, потому что ваше решение использовало Date для сортировки, и мои объекты post не имели свойства даты изначально. (Глупый API ..). Хотел бы я согласиться и с тобой. – Oscar Apeland 11 January 2016 в 01:07
  • 2
    Это пункт в моем последнем примере: с помощью настраиваемого равенства вы также можете проверить индекс в массиве. Кроме того, объект-оболочка может иметь свойство хранить индекс в массиве. – vikingosegundo 11 January 2016 в 01:10
Другие вопросы по тегам:

Похожие вопросы: