Extending Dictionary with constraints
import XCPlayground
import Foundation
protocol CalendarAsDictKey: Hashable {}
extension NSCalendarUnit: CalendarAsDictKey {
public var hashValue: Int {
get {
return Int(self.rawValue)
}
}
}
extension Dictionary where Value: AnyObject, Key: CalendarAsDictKey {
func myFunc() ->Void {
self.forEach {key,value in
print(key, value)
}
}
}
let day = NSCalendarUnit.Day
let era = NSCalendarUnit.Era
let dict = [day: "day", era: NSString(string: "era")]
dict.myFunc()
/*
NSCalendarUnit(rawValue: 16) day
NSCalendarUnit(rawValue: 2) era
*/
How to extend values in dictionaries using key in Python
You should initialize d
as an empty dict instead, so that you can iterate through l
and the key-value pairs to keep appending the values to the sub-list of d
at the given keys:
l = [
{'a': (23, 48), 'b': 34, 'c': "fame", 'd': "who"},
{'a': (94, 29), 'b': 3, 'c': "house", 'd': "cats"},
{'a': (23, 12), 'b': 93, 'c': "imap", 'd': "stack"},
]
d = {}
for s in l:
for k, v in s.items():
d.setdefault(k, []).append(v)
d
becomes:
{'a': [(23, 48), (94, 29), (23, 12)],
'b': [34, 3, 93],
'c': ['fame', 'house', 'imap'],
'd': ['who', 'cats', 'stack']}
If the sub-dicts in l
may contain other keys, you can instead initialize d
as a dict of empty lists under the desired keys:
l = [
{'a': (23, 48), 'b': 34, 'c': "fame", 'd': "who"},
{'a': (94, 29), 'b': 3, 'c': "house", 'd': "cats"},
{'a': (23, 12), 'b': 93, 'c': "imap", 'd': "stack"},
{'e': 'choices'}
]
d = {k: [] for k in ('a', 'b', 'c', 'd')}
for s in l:
for k in d:
d[k].append(s.get(k))
in which case d
becomes:
{'a': [(23, 48), (94, 29), (23, 12), None],
'b': [34, 3, 93, None],
'c': ['fame', 'house', 'imap', None],
'd': ['who', 'cats', 'stack', None]}
Add constraints to generic parameters in extension
Try this code in the Playground:
// make sure only `Optional` conforms to this protocol
protocol OptionalEquivalent {
typealias WrappedValueType
func toOptional() -> WrappedValueType?
}
extension Optional: OptionalEquivalent {
typealias WrappedValueType = Wrapped
// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}
extension Dictionary where Value: OptionalEquivalent {
func flatten() -> Dictionary<Key, Value.WrappedValueType> {
var result = Dictionary<Key, Value.WrappedValueType>()
for (key, value) in self {
guard let value = value.toOptional() else { continue }
result[key] = value
}
return result
}
}
let a: [String: String?] = ["a": "a", "b": nil, "c": "c", "d": nil]
a.flatten() //["a": "a", "c": "c"]
Because you cannot specify an exact type in the where
clause of a protocol extension, one way you may detect exactly the Optional
type is to make Optional
UNIQUELY conforms to a protocol (say OptionalEquivalent
).
In order to get the wrapped value type of the Optional
, I defined a typealias WrappedValueType
in the custom protocol OptionalEquivalent
and then made an extension of Optional, assgin the Wrapped
to WrappedValueType
, then you can get the type in the flatten method.
Note that the sugarCast
method is just to cast the Optional<Wrapped>
to Wrapped?
(which is exactly the same thing), to enable the usage guard
statement.
UPDATE
Thanks to Rob Napier 's comment I have simplified & renamed the sugarCast() method and renamed the protocol to make it more understandable.
Sorting dictionary by value in python under multiple constraints
Tell
key
to ignore the first element using slicing to create a new tuple without the first element:dict(sorted(hmap.items(), key=lambda x: x[1][1:], reverse=True))
outputs
{
5: (70, 90, 160),
4: (60, 90, 150),
6: (70, 80, 150),
3: (80, 50, 130),
1: (80, 40, 120),
2: (60, 20, 80)
}
Manually specify the condition inside the lambda by building the tuple with a minus sign before the value of the key (
x[0]
in this case):dict(sorted(hmap.items(), key=lambda x: (x[1][0], -x[0]), reverse=True))
outputs
{
1: (80, 40, 120),
3: (80, 50, 130),
5: (70, 90, 160),
6: (70, 80, 150),
2: (60, 20, 80),
4: (60, 90, 150)
}
Swift Dictionary With Expansive Type Constraints
If you want to a Dictionary
where the value can be a String
, an Int
, a Bool
or a MyClass
you should
Define your protocol
protocol MyDictionaryValue { }
Conform String, Int, Bool and MyClass to MyDictionaryValue
extension String: MyDictionaryValue { }
extension Int: MyDictionaryValue { }
extension Bool: MyDictionaryValue { }
extension MyClass: MyDictionaryValue { }
Declare your dictionary
var dict = [String:MyDictionaryValue]()
Usage
dict["a"] = "a string"
dict["b"] = 1
dict["c"] = true
dict["d"] = MyClass()
dict["e"] = CGPointZero // error
dict["f"] = UIView() // error
How can I define an extension to CollectionType so that its methods are available to dictionaries?
This works for my current use case:
extension CollectionType where Self: DictionaryLiteralConvertible, Self.Key: StringLiteralConvertible, Self.Value: Encodable, Generator.Element == (Self.Key, Self.Value) { ... }
Related Topics
Swift: Gradient Splits on Rotation
How to Convert Curl Request to Swift Using Alamofire
Turn Off Touch for Whole Screen, Spritekit, How
How to Resolve Rctpromiseresolveblock After Bftask
Cannot Use Mutating Member on Immutable Value of Type 'string'
Collision Detection Leading to Color Detection
How to Populate Table Rows, Using a [String] Array Sent from iPhone by Watch Connectivity
Got Error: Return an Empty String to Bool
How to Properly Map JSON Properties to Model Properties in Realm.Create
Swiftui - Form with Error Message on Button Press and Navigation
Convert Data to Dispatchdata in Swift 4
Applescript Used in My Cocoa MAC App, Stopped Working in Osx 10.14
How to Add a Storage Reference in Swift for Firestore
Nsundomanager: Capturing Reference Types Possible