Как проверить равенство свойств объекта в массиве объектов. Swift

#arrays #swift #class #contains #equality

#массивы #swift #класс #содержит #равенство

Вопрос:

У меня есть класс с именем Movie, который на данный момент имеет только строковое свойство с именем movieTitle .

У меня есть массив Movie, и использование метода .contains возвращает false, даже если объект с тем же названием находится в массиве. Интересно, что .contains работает на игровой площадке, которую я создал, но не в настройках приложения.

Спасибо за помощь! Я довольно новичок в программировании, поэтому, если вы и ELI5 что-то делаете, это было бы здорово!

Вот фрагмент кода, который у меня есть. В итоге происходит то, что он просто продолжает добавлять одни и те же 10 записей в массив.

 do{
    let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
        if let movieSearch = json["Search"] as? [[String: AnyObject]] {
            for movie in movieSearch {
                if let title = movie["Title"] as? String {
                    let newMovie = Movie(movieTitle: title)!

                    if (!self.movieList.contains(newMovie)) {
                        self.movieList.append(newMovie)
                    }
                    self.tableView.reloadData()
                }
            }
        }
    }catch {
        print("Error with Json: (error)")
    }
  

Класс Movie

 import UIKit

class Movie: NSObject, NSCoding {
    // MARK: Properties

    struct PropertyKey {
        static let movieTitleKey = "title"
    }

    // MARK: Archiving Paths

    static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.appendingPathComponent("Movies")

    var movieTitle: String

    // MARK: Initialization

    init?(movieTitle: String) {
        // Initialize stored properties.
        self.movieTitle = movieTitle

        super.init()

        // Initialization should fail if there is no itemName
        if movieTitle.isEmpty {
            return nil
        }
    }

    // MARK: NSCoding

    func encode(with aCoder: NSCoder) {
        aCoder.encode(movieTitle, forKey: PropertyKey.movieTitleKey)
    }

    required convenience init?(coder aDecoder: NSCoder) {
        let title = aDecoder.decodeObject(forKey: PropertyKey.movieTitleKey) as! String

        //Must call designated initializer.
        self.init(movieTitle: title)

    }
}


// MARK: Equatable
func ==(lhs: Movie, rhs: Movie) -> Bool { // Implement Equatable
    return lhs.movieTitle == rhs.movieTitle
}
  

Что работает на игровых площадках

 class Movie: NSObject {
    var movieTitle: String

    init?(movieTitle: String) {
        // Initialize stored properties.
        self.movieTitle = movieTitle

        super.init()

        // Initialization should fail if there is no itemName
        if movieTitle.isEmpty {
            return nil
        }

    }
}

var movieList = [Movie]()

var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")

movieList.append(movie1!)
movieList.append(movie2!)

movieList.contains(movie1!) // Returns True
movieList.contains(movie3!) // Returns False
  

Ответ №1:

Поскольку ваш Movie класс (почему это класс?) наследует от NSObject (почему?), Он наследует NSObject соответствие Equatable протокола NSObject реализации == . По умолчанию выполняется сравнение идентификаторов (сравнение ссылок), а не сравнение значений.

Вот пример:

 let movie1 = Movie(movieTitle: "Batman")
let movie2 = Movie(movieTitle: "Batman")

var movieList = [Movie1]

movieList.contains(movie1!) // True, because movie1 was added in
movieList.contains(movie2!) // False, movie2 was never added
  

Поскольку Movie он не переопределяется == реализацией, которая сравнивает его значения (значения) (например, movieTitle ), он откладывает реализацию по умолчанию, которая сравнивает ссылки. Несмотря movie2 на то, что имеет то же value самое, это отдельный объект со своей собственной (отдельной) ячейкой памяти. Таким образом, сравнение идентификаторов завершается неудачно, и оно не найдено.

Чтобы решить эту проблему, реализуйте == возврат true, если все поля Movie совпадают. Однако то, что вы пытаетесь сделать, может быть лучше реализовать с помощью структур.

Комментарии:

1. Вероятно, это потому, что происходит «сравнение ссылок», потому что func ==(lhs: Movie, rhs: Movie) -> Bool { // Реализовать эквивалентный возврат lhs.movieTitle == rhs.movieTitle }

2. @Alexander вы спрашиваете, почему это класс… Было struct бы лучше для этого случая? Мне просто любопытно

3. @Alexander Что касается того, почему это класс, я расширяю учебник, созданный Apple, и они случайно использовали класс. Мне любопытно, почему структура также была бы лучше

Ответ №2:

вы должны попробовать этот способ.

 var filtered = [Movie]()
filtered = movieList.filter({$0.movieTitle == "Superman"})
if filtered.count == 1 {
//so,"Superman" movie contained in array..
}
  

дайте мне знать результаты … спасибо.

Комментарии:

1. Гораздо предпочтительнее правильно реализовать ==

Ответ №3:

Просто попробуйте этот код, он работает отлично.

 do{
    let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String: AnyObject]
        if let movieSearch = json["Search"] as? [[String: AnyObject]] {
            for movie in movieSearch {
                if let title = movie["Title"] as? String {
                    let newMovie = Movie(movieTitle: title)!

                    let movieTitles = (self.movieList as NSArray).value(forKeyPath: "movieTitle") as? [String]
                    if movieTitles == nil || movieTitles!.contains(title) == false {
                        self.movieList.append(newMovie)
                    }
                    self.tableView.reloadData()
                }
            }
        }
    }catch {
        print("Error with Json: (error)")
    }
  

Ответ №4:

Попробуйте переопределить isEqual метод NSObject , поскольку он уже соответствует Equatable протоколу. Вы можете протестировать приведенный ниже код на игровой площадке. Надеюсь, это поможет.

 class Movie: NSObject {
    var movieTitle: String

    init?(movieTitle: String) {
        // Initialize stored properties.
        self.movieTitle = movieTitle

        super.init()

        // Initialization should fail if there is no itemName
        if movieTitle.isEmpty {
            return nil
        }

    }

    override func isEqual(_ object: Any?) -> Bool {
        guard let theMovie = (object as? Movie) else { return false }
        return movieTitle == theMovie.movieTitle
    }
}

var movieList = [Movie]()

func appendToList(newMovie: Movie) {
    if (!movieList.contains(newMovie)) {
        movieList.append(newMovie)
    }
}

var movie1 = Movie(movieTitle: "Batman")
var movie2 = Movie(movieTitle: "Batman")
var movie3 = Movie(movieTitle: "Superman")

appendToList(newMovie: movie1!)
movieList.count             // count is 1

appendToList(newMovie: movie2!)
movieList.count             // count is still 1 not incremented

movieList.contains(movie1!) // Returns true
movieList.contains(movie2!) // Returns true
movieList.contains(movie3!) // Returns false