Make a dictionary value non-optional as extension - swift

The below playground outlines my issue. The extension will remove nil values from my dictionary, but leave the other values as Optional(Value). What I need is a dictionary that has no nil values and make the optional value type non-optional.
Ex: I have a dictionary of [String:Int?]. I want the jsonSantize() of that called on that dictionary to return a [String:Int].
//: Playground - noun: a place where people can play
import UIKit
import Foundation
protocol OptionalType {
associatedtype Wrapped
var asOptional : Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional : Wrapped? {
return self
}
}
extension Dictionary where Value : OptionalType{
//Sanitizes the current dictionary for json serialization
//Removes nil values entirely. Makes optionals non-optional
func jsonSanitize() -> Dictionary<Key,Value> {
var newDict:[Key:Value] = [:]
for (key, value) in self {
if value.asOptional != nil {
newDict.updateValue(self[key]!, forKey: key)
}
}
return newDict
}
}
var youGood = false
var stringMan:String? = youGood ?
"WOHOO!" :
nil
var dict:[String:Any?] = [
"stuff":"THINGIES",
"things": stringMan
]
var dict2 = dict.jsonSanitize()
print(dict2)
var test = (stringMan != nil)
UPDATE: Suggestion made to use Value.Wrapped as new dictionary type
//: Playground - noun: a place where people can play
import UIKit
import Foundation
protocol OptionalType {
associatedtype Wrapped
var asOptional : Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional : Wrapped? {
return self
}
}
extension Dictionary where Value : OptionalType{
//Sanitizes the current dictionary for json serialization
//Removes nil values entirely. Makes optionals non-optional
func jsonSanitize() -> Dictionary<Key,Value.Wrapped> {
var newDict:[Key:Value.Wrapped] = [:]
for (key, value) in self {
if let v = value.asOptional {
newDict.updateValue(v, forKey: key)
}
}
return newDict
}
}
var youGood = false
var stringMan:String? = youGood ?
"WOHOO!" :
nil
var dict:[String:Any?] = [
"stuff":"THINGIES",
"things": stringMan
]
var dict2:[String:Any] = dict.jsonSanitize()
print(dict2)
var test = (stringMan != nil)

Your method produces a dictionary of the same type [Key: Value]
with Value being some optional type. What you probably want is
to produce a dictionary of type [Key: Value.Wrapped]:
extension Dictionary where Value: OptionalType {
func jsonSanitize() -> [Key: Value.Wrapped] {
var newDict: [Key: Value.Wrapped] = [:]
for (key, value) in self {
if let v = value.asOptional {
newDict.updateValue(v, forKey: key)
}
}
return newDict
}
}
Example:
let dict: [String: Int?] = [
"foo": 1234,
"bar": nil
]
var dict2 = dict.jsonSanitize()
print(dict2) // ["foo": 1234]
Note also that of Swift 3.0.1/Xcode 8.1 beta, optionals
are bridged to NSNull instances automatically, see
SE-0140 – Warn when Optional converts to Any, and bridge Optional As Its Payload Or NSNull

Related

Extension for Array where Element is Optional

I have an array of String? like so
var myArray: [String?] = ["2", "banana", nil, "31"];
I have another array of String like so:
var myStringArray: [String] = ["2", "3"]
I wrote an extension for Array to return me an Optional if index is not valid (which avoids the whole app crashing), but I really only want it to return Optional if the Element is NOT optional, and just return Element if it is Optional
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
If i run this against myArray the resulting type is String??
let item = myArray.safeIndex(1) // item is String??
let strItem = myStringArray.safeIndex(99) // strItem is String?
How do i change my extension such that it gives me String? for BOTH cases
Note: If i am explicit about my types, I can get what I want like so
extension Array where Element == String? {
func safeIndex(_ index: Int) -> String? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
However, this approach requires me to write a bunch of extensions for a bunch of types i might have in an array which is a massive meh.
This is kind of tricky but you can create an AnyOptional protocol that requires an associatedtype (Wrapped) and a computed property to return the optional type. Then you can return the element unwrapped if the index is valid otherwise return nil.
protocol AnyOptional {
associatedtype Wrapped
var optional: Optional<Wrapped> { get }
}
extension Optional: AnyOptional {
var optional: Optional<Wrapped> { self }
}
extension Collection {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
extension Collection {
subscript(safe index: Index) -> Element.Wrapped? where Element: AnyOptional {
indices.contains(index) ? self[index].optional ?? nil : nil
}
}
var myArray: [String?] = ["2", "banana", nil, "31"]
var myStringArray: [String] = ["2", "3"]
let item = myArray[safe: 1] // item is String?
let strItem = myStringArray[safe: 99] // strItem is String?
I think what you want could be achieved with two extensions and a dummy protocol, which would constrain the array's Element. Something like that:
protocol OptionalType {}
extension Optional: OptionalType {}
extension Array where Element: OptionalType {
func safeIndex(_ index: Int) -> Element {
return self[index]
}
}
extension Array {
func safeIndex(_ index: Int) -> Element? {
if self.indices.contains(index) {
return self[index]
}
return nil
}
}
Then this would work:
var myArray: [String?] = ["2", "banana", nil, "31"];
var myStringArray: [String] = ["2", "3"]
let item = myArray.safeIndex(1)
print(type(of: item)) // Optional<String>
let strItem = myStringArray.safeIndex(99)
print(type(of: strItem)) // Optional<String>

Is it possible to write an extension for a specific swift Dictionary ... [String : AnyObject]? [duplicate]

I am trying to create a dictionary extension where Dictionary is of the type <String, AnyObject>.
Was looking in many places and trying different approaches, but none of them seemed to work. This was one of them:
extension Dictionary where <String, AnyObject>{
var jsonString:String {
return ""
}
}
Another method that didn't actually work for some reason:
extension Dictionary where Key:Hashable, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Got: Argument type 'Dictionary' does not conform to expected type of '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 be Hashable, 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 3 Approach
extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject
As StringLiteralConvertible is now deprecated and replaced by ExpressibleByStringLiteral
Update for Swift 3
Here is my example using ExpressibleByStringLiteral for Key and Any for Value.
extension Dictionary where Key: StringLiteralConvertible, Value: Any {
var jsonString: String? {
if let dict = (self as AnyObject) as? Dictionary<String, AnyObject> {
do {
let data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions(rawValue: UInt.allZeros))
if let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
} catch {
print(error)
}
}
return nil
}
}
and then I use it like this:
let dict: Dictionary<String, AnyObject> = [...]
let jsonString = dict.jsonString
You can convert self to AnyObject or NSObject, both works, then you do unwrap as Dictionary or any other specific type.
Adding to the answer provided by #Logan, for those looking to add custom properties to the string-subscripted Dictionary, that is possible as well (was looking to do this when I came across this SO question):
extension Dictionary where Key: StringLiteralConvertible {
var somePropertyThatIsAColor:UIColor? {
get {
return self["someKey"] as? UIColor
}
set {
// Have to cast as optional Value
self["someKey"] = (newValue as? Value)
}
}
Anyone using [String:Any] instead of Dictionary can use below extension
extension Dictionary where Key == String, Value == Any {
mutating func append(anotherDict:[String:Any]) {
for (key, value) in anotherDict {
self.updateValue(value, forKey: key)
}
}
}
So, Dictionary is:
public struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible {..}
How about a protocol extension? :D
extension CollectionType where Self: DictionaryLiteralConvertible, Self.Key == String, Self.Value == AnyObject, Generator.Element == (Self.Key, Self.Value) {
...
}
I was not able to make any of the offered solutions work in Swift 3, but by taking advantage of bridging between Dictionary and NSDictionary I could make this work:
extension NSDictionary {
var jsonString:String {
do {
let stringData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
if let string = String(data: stringData, encoding: .utf8) {
return string
}
}catch _ {
}
return ""
}
}

Lowercase Dictionary Keys in Swift

I need to convert a Dictionary with mixed case keys in the same exact Dictionary but with only lowercase keys.
Here is my attempt (It works but I found this implementation extremely rough)
extension Dictionary {
func lowercaseKeys()->Dictionary<String, AnyObject>{
var newDictionary = Dictionary<String,AnyObject>()
for k in keys{
if let k_string = k as? String{
newDictionary[k_string.lowercaseString] = self[k] as? AnyObject
}
}
return newDictionary
}
}
Can you suggest a more elegant way to solve this problem?
Changes its own keys without the need of a temporary dictionary ;)
var dict = ["HEJ":"DÅ", "NeJ":"tack"]
for key in dict.keys {
dict[key.lowercaseString] = dict.removeValueForKey(key)
}
print(dict)
[hej: DÅ, nej: tack]
EDIT
I made this extension, its a little dirty for now but I will update it again.
extension Dictionary {
mutating func lowercaseKeys() {
for key in self.keys {
let str = (key as! String).lowercaseString
self[str as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["HeJ":"Då", "nEJ":"taCK!"]
dict.lowercaseKeys()
print(dict)
["hej": "Då", "nej": "taCK!"]
EDIT 2
extension Dictionary where Key: StringLiteralConvertible {
mutating func lowercaseKeys() {
for key in self.keys {
self[String(key).lowercaseString as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["NamE":"David", "LAST_NAME":"Göransson"]
dict.lowercaseKeys() // Will compile
var dict2 = [0:"David", 0:"Göransson"]
dict2.lowercaseKeys() // Won't compile because Key isn't StringLiteralConvertible
Smth like that?
var dict = ["KEY": "value"]
var newDict = [String: AnyObject]()
for (key, value) in dict {
newDict[key.lowercaseString] = value
}
Arbitur's answer, updated for Swift 3:
extension Dictionary where Key: ExpressibleByStringLiteral {
public mutating func lowercaseKeys() {
for key in self.keys {
self[String(describing: key).lowercased() as! Key] = self.removeValue(forKey: key)
}
}
}
Here is my solution, no force casting, totally safe.
protocol LowercaseConvertible {
var lowercaseString: Self { get }
}
extension String: LowercaseConvertible {}
extension Dictionary where Key: LowercaseConvertible {
func lowercaseKeys() -> Dictionary {
var newDictionary = Dictionary()
for k in keys {
newDictionary[k.lowercaseString] = self[k]
}
return newDictionary
}
}
Just as an alternative to looping, you could use the map function:
func lowercaseKeys () -> Dictionary<String, AnyObject> {
var newDictionary = Dictionary<String,AnyObject>()
Array(keys).map { key in
newDictionary[(key as! String).lowercaseString] = self[key] as? AnyObject
}
return newDictionary
}

extension of Dictionary where <String, AnyObject>

I am trying to create a dictionary extension where Dictionary is of the type <String, AnyObject>.
Was looking in many places and trying different approaches, but none of them seemed to work. This was one of them:
extension Dictionary where <String, AnyObject>{
var jsonString:String {
return ""
}
}
Another method that didn't actually work for some reason:
extension Dictionary where Key:Hashable, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Got: Argument type 'Dictionary' does not conform to expected type of '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 be Hashable, 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 3 Approach
extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject
As StringLiteralConvertible is now deprecated and replaced by ExpressibleByStringLiteral
Update for Swift 3
Here is my example using ExpressibleByStringLiteral for Key and Any for Value.
extension Dictionary where Key: StringLiteralConvertible, Value: Any {
var jsonString: String? {
if let dict = (self as AnyObject) as? Dictionary<String, AnyObject> {
do {
let data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions(rawValue: UInt.allZeros))
if let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
} catch {
print(error)
}
}
return nil
}
}
and then I use it like this:
let dict: Dictionary<String, AnyObject> = [...]
let jsonString = dict.jsonString
You can convert self to AnyObject or NSObject, both works, then you do unwrap as Dictionary or any other specific type.
Adding to the answer provided by #Logan, for those looking to add custom properties to the string-subscripted Dictionary, that is possible as well (was looking to do this when I came across this SO question):
extension Dictionary where Key: StringLiteralConvertible {
var somePropertyThatIsAColor:UIColor? {
get {
return self["someKey"] as? UIColor
}
set {
// Have to cast as optional Value
self["someKey"] = (newValue as? Value)
}
}
Anyone using [String:Any] instead of Dictionary can use below extension
extension Dictionary where Key == String, Value == Any {
mutating func append(anotherDict:[String:Any]) {
for (key, value) in anotherDict {
self.updateValue(value, forKey: key)
}
}
}
So, Dictionary is:
public struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible {..}
How about a protocol extension? :D
extension CollectionType where Self: DictionaryLiteralConvertible, Self.Key == String, Self.Value == AnyObject, Generator.Element == (Self.Key, Self.Value) {
...
}
I was not able to make any of the offered solutions work in Swift 3, but by taking advantage of bridging between Dictionary and NSDictionary I could make this work:
extension NSDictionary {
var jsonString:String {
do {
let stringData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
if let string = String(data: stringData, encoding: .utf8) {
return string
}
}catch _ {
}
return ""
}
}

Swift check if value is of type array (of any type)

How can I check in Swift if a value is an Array. The problem is that an array of type Int can apparently not be casted to an array of type Any. Suppose I have an array myArray of type Int and execute the following:
if let array = myArray as? [Any] { return true }
it doesn't return true (which surprises me actually). The same thing appears with dictionaries. I want a dictionary of type String, Any (meaning that Any can be any type). How can I check if it is?
Thanks in advance.
Got it working like this, although it's not as beautiful as I would've hoped:
protocol ArrayType {}
extension Array : ArrayType {}
let intArray : Any = [1, 2, 3]
let stringArray : Any = ["hi", "hello", "world"]
intArray is ArrayType // true
stringArray is ArrayType // true
EDIT: I think I misunderstood your question before, now I got it though:
let intArray = [1, 2, 3]
let anyArray = intArray.map{ $0 as Any }
This is the only way to my knowledge.
You can simply
array is Array<Any>
Got it now, with the idea of #Kametrixom. It's extraordinary ugly but at least it works.
private protocol CJSONArrayType {
func toAny() -> [Any]
}
extension Array: CJSONArrayType {
private func toAny() -> [Any] {
return self.map { $0 as Any }
}
}
extension NSMutableArray: CJSONArrayType { }
extension NSArray: CJSONArrayType {
private func toAny() -> [Any] {
var result = [Any]()
for item in self {
result.append(item as Any)
}
return result
}
}
private protocol CJSONDictionaryType {
func toStringAny() -> [String: Any]
}
extension Dictionary: CJSONDictionaryType {
private func toStringAny() -> [String : Any] {
var result = [String: Any]()
for item in self {
result[item.0 as! String] = item.1 as Any
}
return result
}
}
extension NSMutableDictionary: CJSONDictionaryType { }
extension NSDictionary: CJSONDictionaryType {
private func toStringAny() -> [String : Any] {
var result = [String: Any]()
for item in self {
result[item.0 as! String] = item.1 as Any
}
return result
}
}
If you want to parse JSON, there are only a few supported types which are at least AnyObject rather than Any.
Then it's very easy to check for Array
func checkArray(item : AnyObject) -> Bool {
return item is Array<AnyObject>
}
let integer = 1
let string = "one"
let array = ["one", "two", "three"]
let dictionary = ["one" : 1, "two" : 2]
checkArray(integer) // false
checkArray(string) // false
checkArray(array) // true
checkArray(dictionary) // false
Apple highly recommends to constrain the types at compile time as much as possible