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) { ... }
Extending an array of dictionaries using Swift 2.2
Xcode 8 beta • Swift 3
protocol Encodable {
var hashValue: Int { get }
}
extension Int: Encodable { }
extension Collection where Iterator.Element == [String: Encodable] {
var total: Int {
return reduce(0) {$0 + $1.values.reduce(0) {$0 + $1.hashValue}}
}
}
let dictionaries:[[String: Encodable]] = [["One":1],["Two":2],["Three":3, "Four":4]]
dictionaries.total // 10
Xcode 7.3.1 • Swift 2.2.1
protocol Encodable {
var hashValue: Int { get }
}
extension Int: Encodable { }
extension CollectionType where Generator.Element == [String: Encodable] {
var total: Int {
return reduce(0) {$0 + $1.values.reduce(0) {$0 + $1.hashValue}}
}
}
let dictionaries:[[String: Encodable]] = [["One":1],["Two":2],["Three":3, "Four":4]]
dictionaries.total // 10
CollectionType extension with return Type Self instead of Array
There is no promise that an arbitrary CollectionType
can be instantiated or copied. So there's no way to implement your extension. A good way to see this is to try to implement this for FlattenCollection
or LazyCollection
. Or try creating a RandomValueCollection
and then try to implement this method.
But you're free to do this on a RangeReplaceableCollectionType
, which makes all the promises you need:
extension RangeReplaceableCollectionType {
func otherMap(block: Generator.Element -> Generator.Element) -> Self {
var copy = Self.dynamicType.init()
self.forEach {
copy.append(block($0))
}
return copy
}
}
Not all collections conform to RangeReplaceableCollectionType
. There probably is a good reason Set
doesn't, so you may have to create a simpler protocol of your own.
Remember, a CollectionType
may represent some static thing that isn't meaningful or safe to copy. For example, I might make a CollectionType
that represents "the files on this disk." Copying it could invalidate invariants (such as assumptions about caches and file pointers). There's no promise that a CollectionType
is a value type. It is free to be a reference.
Extending CollectionType in Swift 2
'Key' and 'Value' are placeholders in Dictionary, so you can't access these in the method signature for a CollectionType protocol extension - so to get this to work you have to do a cast.
It seems slightly wrong to me, but I think you can just about get it to work with something like this:
extension Dictionary : CollectionAccess {
func getElement<T : Hashable>(key: T) -> Dictionary.Generator.Element? {
if key.self is Key.Type {
let tKey = key as! Key
if let value = self[tKey] {
return (tKey, value)
}
}
return nil
}
}
The thing is, CollectionType doesn't have a placeholder that corresponds to Dictionary's 'Key' placeholder. Not all CollectionType's consist of key value pairs, so therefore it doesn't make sense to make CollectionType have a Key / Value type method. For example another CollectionType is Array - what would getElement return for an array?
extension of Dictionary where String, AnyObject
>=3.1
From 3.1, we can do concrete extensions, ie:
extension Dictionary where Key == String {}
<3.1
We can not conform concrete types w/ concrete generics, ie:
extension Dictionary where Key == String
However, because Dictionary conforms to sequence and we can conform protocol types w/ concrete generics, we could do:
extension Sequence where Iterator.Element == (key: String, value: AnyObject) {
func doStuff() {...
Otherwise, we can constrain our key to a protocol that string conforms to like this:
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
var jsonString: String {
return ""
}
}
As per your updated answer. Json serialization needs an object, Swift Dictionaries are structs. You need to convert to an NSDictionary
You must specify Key
to conform to NSObject
to properly convert to an NSDictionary
.
Small note: Dictionaries already type constrain
Key
to beHashable
, so your original constraint wasn't adding anything.
extension Dictionary where Key: NSObject, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self as NSDictionary, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Note, that the dictionaries must conform to this type to access the extension.
let dict = ["k" : "v"]
Will become type [String : String]
, so you must be explicit in declaring:
let dict: [NSObject : AnyObject] = ["k" : "v"]
Swift Array of dictionaries extension sortInPlace
First issue: you are comparing strings, not dates. Fortunately, your string's format make it directly comparable as both a string and the date value it represents. Hence, you don't need to convert it to NSDate
at all.
The second issue is that typecasting Dictionary<Key,Value1>
to Dictionary<Key,Value2>
wholesale doesn't work, even when Value1
is covariant on Value2
. It may work in trivial example like this...
let a : [String: String] = ["name": "David", "location": "Chicago"]
let b = a as [String: AnyObject]
...because the compiler can see the value type (String
) in a
and optimize it by compile time. For dynamic situations like yours, there's no information available ahead of time.
When you need dynamism, you can always go back to the old friend, Objective-C:
extension Array where Element: CollectionType {
mutating func sortDictionariesByDate(dateKey: String) {
self.sortInPlace {
if let a = $0 as? NSDictionary, b = $1 as? NSDictionary {
return (a[dateKey] as? String) < (b[dateKey] as? String)
} else {
return false
}
}
}
}
// Example:
var sampleArray1: [[String: AnyObject]] = [
["date": "2015-10-24T13:00:00.000Z", "foo": "bar"],
["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]
var sampleArray2: [[String: String]] = [
["date": "2015-10-24T13:00:00.000Z", "foo": "bar"],
["date": "2015-10-24T14:00:00.000Z", "foo": "bar"],
["date": "2015-10-24T10:00:00.000Z", "foo": "bar"]
]
sampleArray1.sortDictionariesByDate("date")
sampleArray2.sortDictionariesByDate("date")
Note that since you are now comparing strings rather than date, no NSDateFormatter
is needed.
Adding an element to a collection type without changing the collection type in Python
You could do this and it will work for all of the standard collection types:
def add_item(self, item):
# You could use append here, and it'd be faster
temporary_list = list(self.items) + [item]
self.items = type(self.items)(temporary_list)
This works because type(x)
returns the "type" of that object. To extract out the actual type string ("list", "set", "tuple", etc.), we have to do some string manipulation. We can then construct a statement equivalent to:
# For lists
self.items = list(temporary_list)
# For sets
self.items = set(temporary_list)
# And so on...
EDIT: I have no idea why I was downvoted. Stackoverflow advises against downvoting without explaining why, just so you know.
Single extension method on IDictionaryK, IEnumerable/IList/ICollectionV
Because all collections implement IEnumerable<T>
, we can just use it instead of the TCollection
type paramter. Unfortunately the type inference does not know this. This is the code I wrote:
public static ILookup<TKey, TValue> ToLookup<TKey, TValue>
(this IDictionary<TKey, IEnumerable<TValue>> dict)
{
return dict.SelectMany(p => p.Value.Select
(v => new KeyValuePair<TKey, TValue>(p.Key, v)))
.ToLookup(p => p.Key, p => p.Value);
}
There seems to be no way of making the type inference work, but this method will work if you cast the Dictionary:
((IDictionary<int, IEnumerable<int>>)dictOfLists).ToLookup()
Also you can add Lists to a Dictionary of IEnumerables and cast them back when you need them.
Related Topics
Float Is Not Convertible to 'Mirrordisposition' Swift What Is Mirrordisposition
Swift 3/Macos: Open Window on Certain Screen
Spritekit Particle Emitter Multi Image
How to Add Floating Button on Top of the Uitableview
How to Integrate Uisearchcontroller with Swiftui
Prevent Error "Funk" Sound in Event Monitor Os X
How to Test Asynchronous Methods Swift
Should the Firebase Object Be a Singleton in Swift
Type '()' Cannot Conform to 'View'
How to Load the Photo Library into Uicollectionview? Swift
Swift Convert Decimal Coordinate into Degrees, Minutes, Seconds, Direction
How to Add Uivibrancyeffect to an Existing Uilabel ( Iboutlet )
Swift Find Superview of Given Class with Generics
Does Untimeintervalnotificationtrigger Nexttriggerdate() Give the Wrong Date
Convert JSON Anyobject to Int64
Swift Protocol with Associated Type - Type May Not Reference Itself as a Requirement