Removing Duplicates From Array of Custom Objects Swift

23,454

Solution 1

You can do it with a set of strings, like this:

var seen = Set<String>()
var unique = [DisplayMessage]
for message in messagesWithDuplicates {
    if !seen.contains(message.id!) {
        unique.append(message)
        seen.insert(message.id!)
    }
}

The idea is to keep a set of all IDs that we've seen so far, go through all items in a loop, and add ones the IDs of which we have not seen.

Solution 2

Here is an Array extension to return the unique list of objects based on a given key:

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

for your example do:

let uniqueMessages = messages.unique{$0.id ?? ""}

Solution 3

Here is an Array extension to return the unique list of objects based on a keyPath:

extension Array {

    func uniques<T: Hashable>(by keyPath: KeyPath<Element, T>) -> [Element] {
        return reduce([]) { result, element in
            let alreadyExists = (result.contains(where: { $0[keyPath: keyPath] == element[keyPath: keyPath] }))
            return alreadyExists ? result : result + [element]
        }
    }
}

Usage:

myChats.uniques(by: \.id)

Solution 4

Create a free duplicate version of an Array, using equality comparisons based on a given key

public extension Sequence {

    public func uniq<Id: Hashable >(by getIdentifier: (Iterator.Element) -> Id) -> [Iterator.Element] {
        var ids = Set<Id>()
        return self.reduce([]) { uniqueElements, element in
            if ids.insert(getIdentifier(element)).inserted {
                return uniqueElements + CollectionOfOne(element)
            }
            return uniqueElements
        }
    }


   public func uniq<Id: Hashable >(by keyPath: KeyPath<Iterator.Element, Id>) -> [Iterator.Element] {
      return self.uniq(by: { $0[keyPath: keyPath] })
   }
}

public extension Sequence where Iterator.Element: Hashable {

    var uniq: [Iterator.Element] {
        return self.uniq(by: { (element) -> Iterator.Element in
            return element
        })
    }

}

Usage

let numbers =  [1,2,3,4,5,6,7,1,1,1,]
let cars = [Car(id:1), Car(id:1), Car(id:2)]

numbers.uniq
cars.uniq(by: { $0.id})
cars.uniq(by: \Car.id)
cars.uniq(by: \.id)
Share:
23,454

Related videos on Youtube

Alk
Author by

Alk

Updated on October 15, 2020

Comments

  • Alk
    Alk over 3 years

    I have a custom class defined as follows :

    class DisplayMessage : NSObject {
    var id : String?
    var partner_image : UIImage?
    var partner_name : String?
    var last_message : String?
    var date : NSDate?
    }
    

    Now I have an array myChats = [DisplayMessage]?. The id field is unique for each DisplayMessage object. I need to check my array and remove all duplicates from it, essentially ensure that all objects in the array have a unique id. I have seen some solutions using NSMutableArray and Equatable however I'm not sure how to adapt them here; I also know of Array(Set(myChats)) however that doesn't seem to work for an array of custom objects.

  • Pascale Beaulac
    Pascale Beaulac almost 7 years
    using a dictionary would be simpler no?
  • ChuckZHB
    ChuckZHB over 4 years
    So what is the O(N) for this solution?
  • Sergey Kalinichenko
    Sergey Kalinichenko over 4 years
    @ChuckZHB The complexity of this solution is O(N) where N is the number of items in the messagesWithDuplicates collection, because checking a hash set and inserting into a hash set has an amortized cost of O(1).
  • ChuckZHB
    ChuckZHB over 4 years
    great! I has added this solution into my App. Thank you.
  • luckyShubhra
    luckyShubhra about 3 years
    this is the best answer :)