Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

There is a bug in "Equatable" when the value is Array or Dictionary. #59

Open
623637646 opened this issue Jun 7, 2021 · 1 comment
Open
Assignees
Labels
bug Something isn't working

Comments

@623637646
Copy link

623637646 commented Jun 7, 2021

print(AnyCodable.init(["1"]) == AnyCodable.init(["1"])) // print false
print(AnyCodable.init(["1:2"]) == AnyCodable.init(["1:2"])) // print false

I am using AnyCodable-FlightSchool 0.6.0

@623637646 623637646 changed the title There is a bug in "Equatable". There is a bug in "Equatable" when the value is Array or Dictionary. Jun 7, 2021
@minacle minacle self-assigned this Jun 8, 2021
@minacle minacle added the bug Something isn't working label Jun 8, 2021
@ph4r05
Copy link

ph4r05 commented Sep 22, 2021

@623637646 I solved deep comparison of [String: AnyCodable]s with nested types in the following way:

func eqAny(_ lhs: Any?, _ rhs: Any?) -> Bool {
 if lhs == nil && rhs == nil { return true }
        if (lhs == nil) != (rhs == nil) { return false }

        var lhsOut = lhs
        var rhsOut = rhs
        while(true) {
            if let x = lhsOut as? AnyCodable {
                lhsOut = x.value
            } else { break }
        }

        while(true) {
            if let x = rhsOut as? AnyCodable {
                rhsOut = x.value
            } else { break }
        }

        switch (lhsOut, rhsOut) {
        case is (Void, Void):
            return true
        case let (lhs as AnyCodable, rhs as AnyCodable):
            return eqAny(lhs.value, rhs.value)
        case let (lhs as Bool, rhs as Bool):
            return lhs == rhs
        case let (lhs as Int, rhs as Int):
            return lhs == rhs
        case let (lhs as Int8, rhs as Int8):
            return lhs == rhs
        case let (lhs as Int16, rhs as Int16):
            return lhs == rhs
        case let (lhs as Int32, rhs as Int32):
            return lhs == rhs
        case let (lhs as Int64, rhs as Int64):
            return lhs == rhs
        case let (lhs as UInt, rhs as UInt):
            return lhs == rhs
        case let (lhs as UInt8, rhs as UInt8):
            return lhs == rhs
        case let (lhs as UInt16, rhs as UInt16):
            return lhs == rhs
        case let (lhs as UInt32, rhs as UInt32):
            return lhs == rhs
        case let (lhs as UInt64, rhs as UInt64):
            return lhs == rhs
        case let (lhs as Float, rhs as Float):
            return lhs == rhs
        case let (lhs as Double, rhs as Double):
            return lhs == rhs
        case let (lhs as String, rhs as String):
            return lhs == rhs
        case let (d1 as [Any?], d2 as [Any?]):
            return d1.count == d2.count
                    && d1.enumerated().map { off, el in eqAny(el, d2[off]) }.reduce(true) { $0 && $1 }
        case let (d1 as [String: Any?], d2 as [String: Any?]):
            return d1.count == d2.count && d1.keys == d2.keys
                    && d1.map { off, el in eqAny(el, d2[off]!) }.reduce(true) { $0 && $1 }
        default:
            return false
        }
    }


// compare with
eqAny(a, b)

Another, more naive option is to encode both to JSON and compare resulting data.
For this to work, JSON has to be encoded in the same way. Problem can be with a dictionary key ordering. Coder would have to be adapted so it always encodes dictionary keys in a sorted order.

@filblue filblue mentioned this issue Sep 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

Successfully merging a pull request may close this issue.

3 participants