Flight-School / AnyCodable

Type-erased wrappers for Encodable, Decodable, and Codable values

Home Page:https://flight.school/books/codable

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

623637646 opened this issue · comments

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