Extension on swift mutable dictionary - swift

I'm trying to create an extension on mutable dictionaries of types [NSObject:AnyObject]
Here is the syntax for an immutable dictionary extension in Swift for [NSObject:AnyObject]:
extension Dictionary where Key:NSObject, Value:AnyObject {
func addSomething() {
// Fails
self["ExampleKey"] = "ExampleValue"
}
}
However, in this case self cannot be appended to, because the extension works on immutable dictionaries. The question is what syntax is missing in order to make an extension for exclusively mutable dictionaries.
Edit: Updated to address ambiguity
Update:
By adding the mutating prefix to addSomething, I can operate on only mutable dictionarys. Yay! However, the function still is not working
mutating func addSomething() {
// Error: Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'
self["ExampleKey"] = "ExampleValue"
}
If I cast "ExampleKey" to a Key, I get another error:
mutating func addSomething() {
let key = "ExampleKey" as! Key
// Error: Ambiguous reference to member 'subscript'
self[key] = "ExampleValue"
}
Still researching how to get this simple addition to work...

This works
extension Dictionary where Key:NSObject, Value:AnyObject {
mutating func addSomething(forKey key:Key, value: Value) {
self[key] = value
}
}
var dict = [NSString:NSString]()
dict.addSomething(forKey: "test", value: "some test")

For Swift 3+ I'm using this extension:
extension Dictionary where Key == String, Value == Any {
mutating func set(value optionalValue: Any?, forKey key: String) {
guard let value = optionalValue else { return }
self[key] = value
}
}
Cheers!

Related

Most specific generic function not called

I'm using #propertyWrapper to reduce my UserDefaults boilerplate as follows…
enum PreferenceKey: String, CaseIterable {
case enumName, stringName
}
#propertyWrapper
struct Prefs<T> {
let key: PreferenceKey
var wrappedValue: T? {
get {
UserDefaults.object(for: key)
}
set {
UserDefaults.set(newValue, for: key)
}
}
}
struct Preferences {
#Prefs(key: .enumName) static var enumName: Name?
#Prefs(key: .stringName) static var stringName: String?
}
extension UserDefaults {
static func object<T>(for key: PreferenceKey) -> T? {
standard.object(forKey: key.rawValue) as? T
}
static func object<T: RawRepresentable>(for key: PreferenceKey) -> T? where T.RawValue == String {
if let value = standard.object(forKey: key.rawValue) as? String {
return T(rawValue: value)
}
return nil
}
static func set<T: RawRepresentable>(_ value: T, for key: PreferenceKey) {
print("Set Raw Value \(value)")
standard.set(value.rawValue, forKey: key.rawValue)
}
static func set<T>(_ value: T, for key: PreferenceKey) {
print("Set Value \(value)")
standard.set(value, forKey: key.rawValue)
}
}
This works fine when setting a regular property list type…
Preferences.stringName = "Fred"
// Set Value Optional("Fred")
print(Preferences.stringName)
// Optional("Fred")
But when trying to set a value that is RawRepresentable, it fails…
Preferences.enumName = .Fred
// Set Value Optional(__lldb_expr_10.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException
Rather than calling the most specific version of UserDefaults.set(, it calls the non-specific one.
Just calling
UserDefaults.set(Name.Fred, for: .enumName)
works fine. In this case it calls the most specific function.
With further testing, and it seems that this isn't a #propertyWrapper issue. The following top level function also fails to call the more specific generic function. It seems like some type information is being lost somewhere
func set<T>(_ value: T?) {
UserDefaults.set(value, for: .enumName)
}
set(Name.Fred)
// Set Value Optional(__lldb_expr_5.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException
What am I missing? Any thoughts as to how I can resolve this?
What am I missing?
Swift is essentially a statically typed language and selecting which of your function overloads to call is determined at compile time.
In your working example:
UserDefaults.set(Name.Fred, for: .enumName)
the type of the first argument is known by the compiler. This type implements RawRepresentable and the compiler uses that to select the overload you expect.
Now consider your failing example:
func set<T>(_ value: T?) {
UserDefaults.set(value, for: .enumName)
}
set(Name.Fred)
When the compiler compiles the set function the only thing it knows about the argument value is that is has a type which it can reference as T. There are no constraints on T, at runtime a value of any type can be passed, so in determining which overload of UserDefaults.set to compile a call to the compiler can only select the overload which also has no constraints and accepts a value of any type.
Any thoughts as to how I can resolve this?
You already know one solution, you overloaded UserDefaults.set, you could overload your set function. However you might wish to consider your design here in the light of Swift's compile-time resolution of overloads – you may not want layers of overloaded functions calling each other.
HTH

Dictionary as a generic element of Array

I have an extension for an Array:
extension Array where Element == [String:Double] {
func values (keyOrder : [String]) -> [[Double]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}
It works pretty well, but only if Dictionary Key is String and Value is Double. I can imagine this function could work exactly same way for Dictionary of any types, like [AnyHashable:Any] but I have no clue how to define header, is it possible?
One useful trick you can use in situations like this is to move the where clause from the extension declaration to the method declaration. This allows you to introduce new generic placeholders for the nested dictionary's Key and Value placeholder types:
extension Array {
func nestedValues<Key, Value>(orderedBy keys: [Key]) -> [[Value]] where Element == [Key: Value] {
return map { element in
return keys.compactMap { element[$0] }
}
}
}
Use can use value of dictionary double as an Any Type. You can try below code.
extension Array where Element == [String: Any] {
func values (keyOrder : [String]) -> [[Any]] {
return self.map { element in
return (0..<keyOrder.count).compactMap {element[keyOrder[$0]]}
}
}
}

How can I translate this utility function into an extension function?

I wrote this utility function in Swift 4:
func insert<Key, Element>(_ value: Element, into dictionary: inout [Key : Set<Element>], at key: Key) {
if let _ = dictionary[key] {
dictionary[key]?.insert(value)
}
else {
var set = Set<Element>()
set.insert(value)
dictionary[key] = set
}
}
This is used like this:
insert("foo", into: &myDictionary, at: "bar")
... but I want to use it like this:
myDictionary.insert("foo", at: "bar")
I tried declaring it like this:
extension Dictionary where Value == Set<AnyHashable> {
mutating func insert(_ value: Value.Element, at key: Key) { // Error here
if let _ = self[key] {
self[key]?.insert(value)
} else {
var set = Set<Value.Element>() // Error here
set.insert(value)
self[key] = set
}
}
}
... but I get the following errors:
/path/to/Sequence Extensions.swift:2:41: error: 'Element' is not a member type of 'Dictionary.Value'
mutating func insert(_ value: Value.Element, at key: Key) {
~~~~~ ^
Swift.Set:608:22: note: did you mean 'Element'?
public typealias Element = Element
^
Swift._IndexableBase:3:22: note: did you mean '_Element'?
public typealias _Element = Self.Element
/path/to/Sequence Extensions.swift:6:23: error: type 'Value.Element' does not conform to protocol 'Hashable'
var set = Set<Value.Element>()
^
Unfortunately, Swift doesn't currently support parameterised extensions (the ability to introduce type variables in extension declarations), so you cannot currently directly express the notion of "an extension with a constraint to some Set<T>". However, it is a part of the generics manifesto, so hopefully it's something that makes its way into a future version of the language.
Even if your extension with Value constrained to Set<AnyHashable> compiled, it wouldn't be terribly useful. You would need to first convert your desired dictionary to a temporary [Key: Set<AnyHashable>], then call the mutating method on it, and then convert it back to its original type (using as!).
This is because the extension is on a Dictionary with heterogenous Set values. It would've been perfectly legal for the extension method to insert arbitrary Hashable elements into one of the values of the dictionary. But that's not what you wanted to express.
In simple cases, I would argue that there's no need for an extension in the first place. You can just say:
var dict = [String: Set<String>]()
dict["key", default: []].insert("someValue")
using Dictionary's subscript overload that takes a default value, as introduced in SE-0165.
If you still want an extension, I would advise simply making it more generic. For example, instead of constraining Value to Set; constrain it to the protocol SetAlgebra (which Set conforms to).
It represents types that can perform set-like operations, and also derives from ExpressibleByArrayLiteral meaning that you can implement your method using the exact syntax as above:
extension Dictionary where Value : SetAlgebra {
mutating func insert(_ value: Value.Element, at key: Key) {
self[key, default: []].insert(value)
}
}
Although one additional thing to consider here is the copy-on-write behaviour of Swift's collection types such as Set. In the above method, the dictionary will be queried for a given key, giving back either an existing set for that key, or a new empty one. Your value will then be inserted into this temporary set, and it will be re-inserted back into the dictionary.
The use of a temporary here means that if the set is already in the dictionary, the value will not be inserted into it in-place, the set's buffer will be copied first in order to preserve value semantics; which could be a performance concern (this is explored in more detail in this Q&A and this Q&A).
However that being said, I am currently looking to fix this for Dictionary's subscript(_:default:) in this pull request, such that the set can be mutated in-place.
Until fixed though, the solution is to first remove the set from the dictionary before mutating:
extension Dictionary where Value : SetAlgebra {
mutating func insert(_ value: Value.Element, at key: Key) {
var set = removeValue(forKey: key) ?? []
set.insert(value)
self[key] = set
}
}
In which case, the use of an extension is fully justified.
It's worth noting that the use of a protocol constraint here is the general solution (or workaround in some cases) to the problem of not having parameterised extensions. It allows you to realise the placeholders you need as associated types of that protocol. See this Q&A for an example of how you can create your own protocol to serve that purpose.
You could do it using a protocol to identify Sets:
protocol SetType
{
associatedtype Element:Hashable
init()
mutating func insert(_ : Element) -> (inserted: Bool, memberAfterInsert: Element)
}
extension Set:SetType
{}
extension Dictionary where Value : SetType
{
mutating func insert(_ value:Value.Element, at key:Key)
{
var valueSet:Value = self[key] ?? Value()
valueSet.insert(value)
self[key] = valueSet
}
}
var oneToMany:[String:Set<String>] = [:]
oneToMany.insert("Dog", at: "Animal")
oneToMany.insert("Cat", at: "Animal")
oneToMany.insert("Tomato", at: "Vegetable")
This will produce a dictionary of sets:
["Animal": Set(["Dog", "Cat"]), "Vegetable": Set(["Tomato"])]
A more appropriate implementation would use the same return value as a Set's insert() function however:
extension Dictionary where Value : SetType
{
#discardableResult
mutating func insert(_ value:Value.Element, at key:Key) -> (inserted: Bool, memberAfterInsert: Value.Element)
{
var valueSet:Value = self[key] ?? Value()
let result = valueSet.insert(value)
if result.inserted
{ self[key] = valueSet }
return result
}
}
[EDIT] I just read all of Hamish's response and realized that he had already given the same answer (essentially) and made use of SetAlgebra ( which I wasn't aware of) that does the same thing as the SetType I "reinvented". You should accept Hamish's answer.

Swift: Optional chaining for optional subscripts

I have a let map : [String: String] and a let key: String?.
What is the most concise way to access map[key] (and get back a String? if I had a key and None if I did not)?
let value = key.flatMap { map[$0] }
would to the trick, using the
/// Returns `nil` if `self` is nil, `f(self!)` otherwise.
#warn_unused_result
public func flatMap<U>(#noescape f: (Wrapped) throws -> U?) rethrows -> U?
method from struct Optional.
Alternatively, you can wrap that into a custom subscript method
extension Dictionary {
subscript(optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
and the simply write
let value = map[key]
To avoid confusion with the "normal" subscript method, and to make
the intention more clear to the reader of your code, you can define
the subscript method with an external parameter name:
extension Dictionary {
subscript(optional optKey : Key?) -> Value? {
return optKey.flatMap { self[$0] }
}
}
let value = map[optional: key]
I think I am going with the decidedly unfancy
key == nil ? nil : map[key!]

Referencing self in Dictionary extension

I'm trying to extend Dictionary but can't reference self with a key. I'm confused as to why this is.
extension Dictionary {
func foo() {
var result = self["key"]
}
}
I get this error :
Type 'DictionaryIndex' does not conform to protocol 'StringLiteralConvertible'
If anyone has any insight, it would be appreciated.
Dictionary is a Generic struct. It is generic on its Key and Value.
Thus in your extension you have to use Key as the type for the dictionary keys, and Value as the type for dictionary values.
The compiler is complaining because you are using the wrong type for the dictionary key extension.
Here is an example:
extension Dictionary {
func ext(key: Key) {
if let value = self[key] {
// use your value
println("Key is present")
} else {
println("No value for key")
}
}
}
let dic = ["A": 20]
dic.ext("A")
dic.ext("B")
Here is how you can do something similar ... it might make clearer why your test didn't work:
extension Dictionary {
var foo: String? {
if let key = "key" as? Key {
return self[key] as? String
}
return nil
}
}
let dic1 = ["A": "an A", "key": "the value"]
dic1.foo // "the value" as optional
dic.foo // nil since dic value type is Int
Since Dictionary is a generic struct, you might reconsider extending it as if it is a specific concrete type.