-3

Dears,

this is my code testable in a playground:

import Foundation
import UIKit

enum Error : Swift.Error, LocalizedError {

    case failedTypeCastingUITableViewCell(name: String, type: Any.Type, reuseIdentifier: String?)

    var domain: String {
        return ""
    }

    var code: Int {
        return 0
    }

    var nserror : NSError {
        return NSError(domain: self.domain,
                       code: self.code,
                       userInfo: self.userInfo)
    }

//    var userInfo : [String: Any]? {
//        var userInfo : [String: Any] = [:]
//
//        switch self {
//        case .failedTypeCastingUITableViewCell(let name, let type, let reuseIdentifier):
//            userInfo = [
//                "param_name": name
//               ,"param_type": "\(type)"
//               ,"param_identifier": reuseIdentifier
//            ]
//            return userInfo
//        }
//    }

    var userInfo : [String: Any]? {
        var userInfo : [String: Any] = [:]

        switch self {
        case .failedTypeCastingUITableViewCell(let name, let type, let reuseIdentifier):
            userInfo = [
                "param_name": name
               ,"param_type": "\(type)"
            ]
            userInfo["param_identifier"] = reuseIdentifier
            return userInfo
        }
    }

}

print(Error.failedTypeCastingUITableViewCell(name: "", type: UITableViewCell.self, reuseIdentifier: nil).userInfo)

this is the result I get from print and is what I want to achieve with commented code:

Optional(["param_name": "", "param_type": "UITableViewCell"])

this is the result I get from commented code instead:

Optional(["param_identifier": nil, "param_type": "UITableViewCell", "param_name": ""])

I know It have to work this way, but my question is can I get rid of this in some way? ie. custom init? custom subscript?

  • 2
    dict.removeValue(forKey: "property_name") – rbaldwin May 21 '20 at 14:47
  • 2
    The syntax you've written in your question assigns the whole dictionary, so it would be exactly equivalent to `dict = [:]`. What behavior are you looking for? Are you expecting this syntax to just remove two values, or to remove all values? (Note that the `Any` type in Swift exists to deal with certain corner cases, particularly interacting with ObjC. It is not intended to be commonly used, and it often requires a lot of extra syntax, particularly to avoid warnings. If you're doing a lot of work with `Any`, you should adjust your types to avoid that. We can help if you have questions.) – Rob Napier May 21 '20 at 14:50
  • I want achieve same result of dictionary["property_name"] = nil with different syntax I'm not trying to solve a real issue, dict.removeValue(forKey:) is not what I'm looking for @rbaldwin – Giuseppe Mazzilli May 21 '20 at 15:10
  • 2
    You should clarify exactly what you are trying to do / expected outcome. – rbaldwin May 21 '20 at 15:12
  • @RobNapier your answer is out of scope or my question wasn't clear enough – Giuseppe Mazzilli May 21 '20 at 15:15
  • @ielyamani struct is not dynamic like dictionary. – Giuseppe Mazzilli May 21 '20 at 15:15
  • @rbaldwin I want a dictionary subscript or a constructor itself capable to handle a syntax like in my example if it is possible. – Giuseppe Mazzilli May 21 '20 at 15:17
  • Updated post hope to be more clear – Giuseppe Mazzilli May 21 '20 at 15:23
  • 1
    Can you give a full example of a real dictionary, what your expected syntax would be, and what that dictionary's value would be afterwards? The two syntaxes you list as "and get the same result" don't seem related. (My suspicion is that the answer to your exact question is just "no." So we're all trying to understand the goal so we can provide a more useful answer.) – Rob Napier May 21 '20 at 15:26
  • This problem also gets much more difficult to solve well if the value type is Any. If you don’t require Any here, then there may be better solutions to you’re underlying problem. – Rob Napier May 21 '20 at 15:30
  • @Rob Napier, i completly rewritten my example so now u can test it – Giuseppe Mazzilli May 21 '20 at 15:59
  • Excellent! That is exactly the kind of question that we can answer. – Rob Napier May 21 '20 at 16:17

2 Answers2

1

Yes! Just don't write anything for that subscript, and when you try to access that subscript, it will automatically return nil.

Example:

myDict: [String : Any] = [
    "key1" : 1,
    "key2" : 2
]

let emptySubscript = myDict["key3"] // this will be nil
Sylvan M.
  • 160
  • 7
1

Given the concrete example, this is certainly possible, and in a number of ways.

One way is to use key/value pairs, and filter out what is nil:

let kv = [
    ("param_name", name),
    ("param_type", "\(type)"),
    ("param_identifier", reuseIdentifier)
    ].filter{$1 != nil}.map{($0, $1!)} // <== this is your magic line

userInfo = Dictionary(uniqueKeysWithValues: kv)

Another way is with reduce:

userInfo = [
    ("param_name", name),
    ("param_type", "\(type)"),
    ("param_identifier", reuseIdentifier)
    ]
    .reduce(into: [:]) { (d, kv) in d[kv.0] = kv.1 }

For more readability, you could extract that to an extension:

extension Dictionary {
    init<S>(uniqueKeysWithNonNilValues seq: S)
        where S : Sequence, S.Element == (Key, Value?) {
            self = seq.reduce(into: [:]) { (d, kv) in d[kv.0] = kv.1 }
    }
}

And then the calling code would be pretty nice IMO:

userInfo = Dictionary(uniqueKeysWithNonNilValues: [
    ("param_name", name),
    ("param_type", "\(type)"),
    ("param_identifier", reuseIdentifier)
])

But we could push it just a little closer to your proposed syntax, and maybe that's nicer because it feels like a Dictionary again:

extension Dictionary {
    init(withNonNilValues seq: KeyValuePairs<Key, Value?>) {
        self = seq.reduce(into: [:]) { (d, kv) in d[kv.0] = kv.1 }
    }
}

userInfo = Dictionary(withNonNilValues: [
    "param_name": name,
    "param_type": "\(type)",
    "param_identifier": reuseIdentifier,
    ])
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    Last one is the winner /clap – Giuseppe Mazzilli May 21 '20 at 16:46
  • btw in Swift 5 you must esplicitate dictionary type or get an error "Heterogeneous collection literal could only be inferred to '[String : Any]'; add explicit type annotation if this is intentional" so correct syntax would be: ``` userInfo = [String: Any](withNonNilValues: [ "param_name": name, "param_type": "\(type)", "param_identifier": reuseIdentifier, ]) ``` – Giuseppe Mazzilli May 21 '20 at 20:15
  • I didn't encounter this issue, but you can fix it more easily by using `[String: String]` rather than `[String: Any]` as much as possible (since your code really is `[String: String]`). In Swift, `Any` is a fairly cursed type, and you should avoid it unless you really need it. The problem is that all Optionals are themselves of type `Any`, so when you have `[String: Any]`, that can accidentally hold Optional nil (or even filled-in Optionals) that you didn't mean. – Rob Napier May 21 '20 at 20:44
  • [String: Any] is needed in this case since dictionary should accept different type of items to be reversed in NSError object and/or directly to Firebase Crashlytics. – Giuseppe Mazzilli May 21 '20 at 20:57