Swift: Removing duplicate objects from array with a UUID

554 Views Asked by At

I am using a UUID to get the use of DiffableDataSource but I am working with a data set that has a duplicate of every object.

Here is a sample of the code I am working with from a playground:

var movies: [MovieSearch] = []

struct MovieSearch: Hashable, Decodable, Equatable {
    let uuid = UUID()
    private enum CodingKeys : String, CodingKey { case Name, StartDate }
    
    let Name: String
    let StartDate: String
}
    
movies = [
  MovieSearch(Name: "Blade Runner", StartDate: "01/01/2021"), 
  MovieSearch(Name: "Blade Runner: 2049", StartDate: "01/07/2021"),     
  MovieSearch(Name: "UBIK", StartDate: "01/14/2021"),    
  MovieSearch(Name: "Blade Runner", StartDate: "01/01/2021"),    
  MovieSearch(Name: "Blade Runner: 2049", StartDate: "01/07/2021"),    
  MovieSearch(Name: "UBIK", StartDate: "01/14/2021")    
]

Since the UUID is added at the time of initialization is there a reasonable way to delete the duplicates in this scenario?

3

There are 3 best solutions below

2
On BEST ANSWER

So I'm guessing what you want to do is to remove those duplicates?

Then write a function that removes the duplicates based on the name of the movie (which I'm guessing is unique) and use it to filter.

func removeDup(movies: [MovieSearch]) -> [MovieSearch] {
    var uniqueMovies = Set<String>()
    var moviesWithoutDups = [MovieSearch]()
    
    for movie in movies {
        if !uniqueMovies.contains(movie.name) {
            moviesWithoutDups.append(movie)
            uniqueMovies.insert(movie.name)
        }
    }
    return moviesWithoutDups
}

Then use the function:

movies = removeDup(movies: movies)
0
On

If you don't care about the order of your Movies, you can use a Swift's Set.

Since your MovieSearch conforms to the Hashable protocol, Set will ensure that only exactly one copy exists.

You can iterate through the Set using for-in notation, similar to an array.

https://developer.apple.com/documentation/swift/set

struct MovieSearch: Hashable, Decodable, Equatable {
    let uuid = UUID()
    private enum CodingKeys : String, CodingKey { case Name, StartDate }
    
    let Name: String
    let StartDate: String
}
    
var movies:Set = [
  MovieSearch(Name: "Blade Runner", StartDate: "01/01/2021"), 
  MovieSearch(Name: "Blade Runner: 2049", StartDate: "01/07/2021"),     
  MovieSearch(Name: "UBIK", StartDate: "01/14/2021"),    
  MovieSearch(Name: "Blade Runner", StartDate: "01/01/2021"),    
  MovieSearch(Name: "Blade Runner: 2049", StartDate: "01/07/2021"),    
  MovieSearch(Name: "UBIK", StartDate: "01/14/2021")    
]

for movie in movies {
   print(movie.Name)
}
4
On

You can use the uniqued method from Apple's swift-algorithms package.

You can see the method's source here.

It allows you to specify which fields in the object you want to compare for duplication. So in your scenario, you can use it as follows:

movies = movies.uniqued(on: \.Name)
print(movies) 

Note: you do need your struct to conform to Hashable to use this (which in this instance you already are conforming to it)

For those that don't want to click the link for some reason. Here's the code provided by Apple:

@inlinable
func uniqued<Subject: Hashable>(
    on projection: (Element) throws -> Subject
) rethrows -> [Element] {
    var seen: Set<Subject> = []
    var result: [Element] = []
    for element in self {
        if seen.insert(try projection(element)).inserted {
            result.append(element)
        }
    }
    return result
}

Edit:

Updated answer with key path in uniqued method. As suggested by Alexander