如何使用 Equatable 比较基于不同属性的自定义对象?
How to compare custom objects based on different properties using Equatable?
我正在使用 equatable 协议来比较基于一个名为 mediaUID
.
的 属性 的两个自定义对象
有没有办法在比较不同的属性之间切换?
在 func fetchNotificationsRemoved
中,有时我需要通过 mediaUID
或 likeUID 进行比较。
var notificationsArray = [NotificationInformation]()
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] (newNotification: NotificationInformation?) in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
if notification.type == "like" {
// How to compare based on likeUID using equatable?
//compare based on likeUID
//get the index of the item of type 'like' in notificationsArray and do something with it
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
}else if notification.type == "media" {
// How to compare based on mediaUID using equatable?
//compare based on mediaUID
//get the index of the item of type 'media' in notificationsArray
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
} else if if notification.type == "commentUID" {
....
}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes,
indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}//end extension
//enables us to compare two objects of type NotificationInformation
extension NotificationInformation: Equatable { }
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return mediaUIDLeft == mediaUIDRight
}
您可以使用 static var
来建立要用于比较的字段:
class NotificationInformation: Equatable {
enum CompareField {
case type, mediaUID, commentUID, likeUID
}
static var compareField: CompareField = .mediaUID
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
init(type: String, mediaUID: String? = nil, commentUID: String? = nil, likeUID: String? = nil) {
self.type = type
self.mediaUID = mediaUID
self.commentUID = commentUID
self.likeUID = likeUID
}
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
switch NotificationInformation.compareField {
case .type:
return lhs.type == rhs.type
case .mediaUID:
return lhs.mediaUID == rhs.mediaUID
case .commentUID:
return lhs.commentUID == rhs.commentUID
case .likeUID:
return lhs.likeUID == rhs.likeUID
}
}
}
示例:
let a = NotificationInformation(type: "foo", mediaUID: "123")
let b = NotificationInformation(type: "bar", mediaUID: "123")
NotificationInformation.compareField = .type
if a == b {
print("same type")
}
NotificationInformation.compareField = .mediaUID
if a == b {
print("same mediaUID")
}
输出:
same mediaUID
使用 OptionSet
比较多个字段
如果将 enum
替换为 OptionSet
,则可以 select 多个字段进行比较:
struct CompareFields: OptionSet {
let rawValue: Int
static let type = CompareFields(rawValue: 1 << 0)
static let mediaUID = CompareFields(rawValue: 1 << 1)
static let commentUID = CompareFields(rawValue: 1 << 2)
static let likeUID = CompareFields(rawValue: 1 << 3)
}
static var compareFields: CompareFields = .mediaUID
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
var equal = true
if NotificationInformation.compareFields.contains(.type) {
equal = equal && (lhs.type == rhs.type)
}
if NotificationInformation.compareFields.contains(.mediaUID) {
equal = equal && (lhs.mediaUID == rhs.mediaUID)
}
if NotificationInformation.compareFields.contains(.commentUID) {
equal = equal && (lhs.commentUID == rhs.commentUID)
}
if NotificationInformation.compareFields.contains(.likeUID) {
equal = equal && (lhs.likeUID == rhs.likeUID)
}
return equal
}
例子
let a = NotificationInformation(type: "foo", mediaUID: "123", commentUID: "111")
let b = NotificationInformation(type: "bar", mediaUID: "123", commentUID: "111")
NotificationInformation.compareFields = .mediaUID
if a == b {
print("same mediaUID")
}
NotificationInformation.compareFields = [.mediaUID, .commentUID]
if a == b {
print("same mediaUID and commentUID")
}
输出
same mediaUID
same mediaUID and commentUID
多线程问题
如果您的代码在另一个线程中修改 compareFields
值,则会出现问题。对于所有线程,equals 的含义都会改变。一种可能的解决方案是仅在主线程中更改和使用 NotificationInformation
的相等性。
...
} else if notification.type == "media" {
DispatchQueue.main.async {
NotificationInformation.compareFields = .mediaUID
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
// use index
...
}
}
...
将您的 func
更改为:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (mediaUIDLeft == mediaUIDRight || lhs.likeUID == rhs.likeUID)
}
This means that two NotificationInformation
are equal if they have the same mediaUID
OR the same likeUID
如果需要条件检查,可以引入布尔变量:
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
let checkByMediaUID: Bool = true
}
所以改变你的Equatable
:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (lhs.checkByMediaUID || rhs.checkByMediaUID) ? mediaUIDLeft == mediaUIDRight : lhs.likeUID == rhs.likeUID
}
以更具可读性的方式:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
if lhs.checkByMediaUID || rhs.checkByMediaUID{
return mediaUIDLeft == mediaUIDRight
}
return lhs.likeUID == rhs.likeUID
}
This means that if you want to check by mediaUID
, just compare two object. If you want to check by likeUID, just change the variable of one of the object.
例子
let a: NotificationInformation = NotificationInformation()
let b: NotificationInformation = NotificationInformation()
//Check by `mediaUID`
if a == b{
....
}
//Check by `likeUID`
a.checkByMediaUID = false
if a == b{
....
}
你可以查看NotificationInformation
个对象的type
属性,并据此比较对象。
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case "like": return lhs.likeUID == rhs.likeUID
case "media": return lhs.mediaUID == rhs.mediaUID
case "commentUID": return lhs.commentUID == rhs.commentUID
default: return false
}
}
}
您可以使用 enum
作为 type
属性
class NotificationInformation {
enum NotificationType: String {
case like
case media
case commentUID
}
let type: NotificationType
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case .like: return lhs.likeUID == rhs.likeUID
case .media: return lhs.mediaUID == rhs.mediaUID
case .commentUID: return lhs.commentUID == rhs.commentUID
}
}
}
用法
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] newNotification in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes, indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}
我正在使用 equatable 协议来比较基于一个名为 mediaUID
.
的 属性 的两个自定义对象
有没有办法在比较不同的属性之间切换?
在 func fetchNotificationsRemoved
中,有时我需要通过 mediaUID
或 likeUID 进行比较。
var notificationsArray = [NotificationInformation]()
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] (newNotification: NotificationInformation?) in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
if notification.type == "like" {
// How to compare based on likeUID using equatable?
//compare based on likeUID
//get the index of the item of type 'like' in notificationsArray and do something with it
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
}else if notification.type == "media" {
// How to compare based on mediaUID using equatable?
//compare based on mediaUID
//get the index of the item of type 'media' in notificationsArray
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
} else if if notification.type == "commentUID" {
....
}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes,
indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}//end extension
//enables us to compare two objects of type NotificationInformation
extension NotificationInformation: Equatable { }
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return mediaUIDLeft == mediaUIDRight
}
您可以使用 static var
来建立要用于比较的字段:
class NotificationInformation: Equatable {
enum CompareField {
case type, mediaUID, commentUID, likeUID
}
static var compareField: CompareField = .mediaUID
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
init(type: String, mediaUID: String? = nil, commentUID: String? = nil, likeUID: String? = nil) {
self.type = type
self.mediaUID = mediaUID
self.commentUID = commentUID
self.likeUID = likeUID
}
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
switch NotificationInformation.compareField {
case .type:
return lhs.type == rhs.type
case .mediaUID:
return lhs.mediaUID == rhs.mediaUID
case .commentUID:
return lhs.commentUID == rhs.commentUID
case .likeUID:
return lhs.likeUID == rhs.likeUID
}
}
}
示例:
let a = NotificationInformation(type: "foo", mediaUID: "123")
let b = NotificationInformation(type: "bar", mediaUID: "123")
NotificationInformation.compareField = .type
if a == b {
print("same type")
}
NotificationInformation.compareField = .mediaUID
if a == b {
print("same mediaUID")
}
输出:
same mediaUID
使用 OptionSet
如果将 enum
替换为 OptionSet
,则可以 select 多个字段进行比较:
struct CompareFields: OptionSet {
let rawValue: Int
static let type = CompareFields(rawValue: 1 << 0)
static let mediaUID = CompareFields(rawValue: 1 << 1)
static let commentUID = CompareFields(rawValue: 1 << 2)
static let likeUID = CompareFields(rawValue: 1 << 3)
}
static var compareFields: CompareFields = .mediaUID
static func ==(lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
var equal = true
if NotificationInformation.compareFields.contains(.type) {
equal = equal && (lhs.type == rhs.type)
}
if NotificationInformation.compareFields.contains(.mediaUID) {
equal = equal && (lhs.mediaUID == rhs.mediaUID)
}
if NotificationInformation.compareFields.contains(.commentUID) {
equal = equal && (lhs.commentUID == rhs.commentUID)
}
if NotificationInformation.compareFields.contains(.likeUID) {
equal = equal && (lhs.likeUID == rhs.likeUID)
}
return equal
}
例子
let a = NotificationInformation(type: "foo", mediaUID: "123", commentUID: "111")
let b = NotificationInformation(type: "bar", mediaUID: "123", commentUID: "111")
NotificationInformation.compareFields = .mediaUID
if a == b {
print("same mediaUID")
}
NotificationInformation.compareFields = [.mediaUID, .commentUID]
if a == b {
print("same mediaUID and commentUID")
}
输出
same mediaUID same mediaUID and commentUID
多线程问题
如果您的代码在另一个线程中修改 compareFields
值,则会出现问题。对于所有线程,equals 的含义都会改变。一种可能的解决方案是仅在主线程中更改和使用 NotificationInformation
的相等性。
...
} else if notification.type == "media" {
DispatchQueue.main.async {
NotificationInformation.compareFields = .mediaUID
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
// use index
...
}
}
...
将您的 func
更改为:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (mediaUIDLeft == mediaUIDRight || lhs.likeUID == rhs.likeUID)
}
This means that two
NotificationInformation
are equal if they have the samemediaUID
OR the samelikeUID
如果需要条件检查,可以引入布尔变量:
class NotificationInformation {
let type: String
let mediaUID: String?
let commentUID: String?
let likeUID:String?
let checkByMediaUID: Bool = true
}
所以改变你的Equatable
:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
return (lhs.checkByMediaUID || rhs.checkByMediaUID) ? mediaUIDLeft == mediaUIDRight : lhs.likeUID == rhs.likeUID
}
以更具可读性的方式:
func ==(lhs: NotificationInformation ,rhs: NotificationInformation) -> Bool {
guard let mediaUIDLeft = lhs.mediaUID else {return false}
guard let mediaUIDRight = rhs.mediaUID else {return false}
if lhs.checkByMediaUID || rhs.checkByMediaUID{
return mediaUIDLeft == mediaUIDRight
}
return lhs.likeUID == rhs.likeUID
}
This means that if you want to check by
mediaUID
, just compare two object. If you want to check by likeUID, just change the variable of one of the object.
例子
let a: NotificationInformation = NotificationInformation()
let b: NotificationInformation = NotificationInformation()
//Check by `mediaUID`
if a == b{
....
}
//Check by `likeUID`
a.checkByMediaUID = false
if a == b{
....
}
你可以查看NotificationInformation
个对象的type
属性,并据此比较对象。
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case "like": return lhs.likeUID == rhs.likeUID
case "media": return lhs.mediaUID == rhs.mediaUID
case "commentUID": return lhs.commentUID == rhs.commentUID
default: return false
}
}
}
您可以使用 enum
作为 type
属性
class NotificationInformation {
enum NotificationType: String {
case like
case media
case commentUID
}
let type: NotificationType
let mediaUID: String?
let commentUID: String?
let likeUID:String?
}
extension NotificationInformation: Equatable {
static func == (lhs: NotificationInformation, rhs: NotificationInformation) -> Bool {
guard lhs.type == rhs.type else {
print("Types of lhs and rhs are not same ")
return false
}
switch lhs.type {
case .like: return lhs.likeUID == rhs.likeUID
case .media: return lhs.mediaUID == rhs.mediaUID
case .commentUID: return lhs.commentUID == rhs.commentUID
}
}
}
用法
extension NotificationInformation {
func fetchNotificationsRemoved(query: DatabaseQuery) {
NotificationInformation.observeNewNotificationsChildRemoved(query: query) { [weak self] newNotification in
guard let strongSelf = self else {return}
guard let notification = newNotification else {return}
guard let index = strongSelf.notificationsArray.index(of: notification) else {return}
strongSelf.notificationsArray.remove(at: index)
let visibleIndexes = strongSelf.tableView.indexPathsForVisibleRows
let indexPathOfRemovedNotification = IndexPath(row: index, section: 0)
if let indexes = visibleIndexes, indexes.contains(indexPathOfRemovedNotification) {
strongSelf.tableView.deleteRows(at: [indexPathOfRemovedNotification], with: .fade)
}
}
}
}