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
}
Related
I am getting an error: 'Any?' is not convertible to 'Profiles' I am not sure how to correct. I am trying to extend the dictionary. I am needing the structure show in the Profile Struct
struct Profiles {
var id: Int
var name: String
}
extension NSDictionary {
var profileDictionary: [String : Profiles] {
var dictionary: [String : Profiles] = [:]
let keys = self.allKeys.compactMap { $0 as? String }
for key in keys {
let keyValue = self.value(forKey: key) as Profiles
dictionary[key] = keyValue
}
return dictionary
}
}
Not sure why you need to use NSDictionary when coding with Swift. I will post the Dictionary approach but NSDictionary would be exactly the same. You can use reduce method and convert all key value pairs to (String, Profiles) and add it to the resulting dictionary:
extension Dictionary {
var profileDictionary: [String : Profiles] {
reduce(into: [:], { result, keyValue in
if let kv = keyValue as? (String, Profiles) {
result[kv.0] = kv.1
}
})
}
}
Since you can't cast a Dictionary<String, AnyObject?> into a NSDictionary directly, I want to write a Dictionary extension that will do that conversion (substituting NSNull for any nils).
But in order to do that, my Dictionary extension must be constrained to dicts whose values are Optional<T> where T:AnyObject. How do I write the type constraint for that?
You can't add nil to a dict. So you have to force it in there with a cast to someType? just so you have a key.
Borrowing from this Q/A for the OptionalConvertible solution:
protocol OptionalConvertible {
typealias WrappedValueType = AnyObject
func toOptional() -> WrappedValueType?
}
extension Optional: OptionalConvertible {
typealias WrappedValueType = Wrapped
// just to cast `Optional<Wrapped>` to `Wrapped?`
func toOptional() -> WrappedValueType? {
return self
}
}
extension Dictionary where Value: OptionalConvertible, Key: NSCopying {
//dict: Dictionary<Key, Optional<Value>>
func convertToNSDictionary() -> NSDictionary {
let mutableDict : NSMutableDictionary = NSMutableDictionary()
for key in self.keys {
if let maybeValue = self[key] {
if let value = maybeValue.toOptional() {
mutableDict[key] = value as? AnyObject
} else {
mutableDict[key] = NSNull()
}
}
}
return mutableDict
}
}
var optionalObject : UIView? = nil
var dict : [NSString:AnyObject?] = [:]
dict["alpha"] = 1
dict["beta"] = 2
dict["delta"] = optionalObject as AnyObject? // force a nil into the dict
dict // ["beta": {Some 2}, "alpha": {Some 1}, "delta": nil]
let nsdict = dict.convertToNSDictionary() // ["alpha": 1, "beta": 2, "delta": {NSObject}]
Or a more practical approach :
extension NSDictionary {
static func fromDictionary<Key: Hashable, Value:AnyObject where Key: NSCopying>(dictionary:Dictionary<Key, Value>) -> NSDictionary {
let mutableDict : NSMutableDictionary = NSMutableDictionary()
for key in dictionary.keys {
if let value = dictionary[key] {
mutableDict[key] = value
} else {
mutableDict[key] = NSNull()
}
}
return mutableDict
}
static func fromDictionary<Key: Hashable, Value:AnyObject where Key: NSCopying>(dict: Dictionary<Key, Optional<Value>>) -> NSDictionary {
let mutableDict : NSMutableDictionary = NSMutableDictionary()
for key in dict.keys {
if let maybeValue = dict[key] {
if let value = maybeValue {
mutableDict[key] = value
} else {
mutableDict[key] = NSNull()
}
}
}
return mutableDict
}
}
I'm experienced in Obj-C, but fairly new to Swift. I have a simple function that takes a Set and a Dictionary as parameters:
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>) {
for item in dataToParse {
for dict in lookupData {
let nameID = dict["name"] // compile error
}
}
}
The passed in parameter lookupData is a dictionary containing nested dictionaries. I know that each of these dictionaries contains a key called name but when I try to access that key using the following syntax:
let nameID = dict["name"]
I get the following comile error:
Type '(String, AnyObject)' has no subscript members
If I know that a key exists, how do I access it? Thanks!
import Foundation
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>)->[AnyObject] {
var ret: Array<AnyObject> = []
for item in dataToParse {
for (key, value) in lookupData {
if key == item {
ret.append(value)
}
}
}
return ret
}
let set = Set(["alfa","beta","gama"])
var data: Dictionary<String,AnyObject> = [:]
data["alfa"] = NSNumber(integer: 1)
data["beta"] = NSDate()
data["theta"] = NSString(string: "some string")
let find = buildSource(set, lookupData: data)
dump(find)
/* prints
▿ 2 elements
- [0]: 28 Nov 2015 18:02
▿ [1]: 1 #0
▿ NSNumber: 1
▿ NSValue: 1
- NSObject: 1
*/
in your code
for dict in lookupData {
let nameID = dict["name"] // compile error
}
dict is not a dictionary, but (key, value) tuple!
Update:
get value from lookupData with "name" as the key
downcast to a dictionary with as? [String:AnyObject]
iterate over dataToParse
use the String from dataToParse to access the nested dictionary.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
guard let dict = lookupData["name"] as? [String:AnyObject] else {
return
}
for item in dataToParse {
let value = dict[item]
}
}
More possible solutions:
If lookupData is an array of dictionaries:
func buildSource(dataToParse:Set<String>, lookupData:[[String:AnyObject]]) {
for item in dataToParse { // String
for dict in lookupData { // dict
let nameID = dict["name"] // value
}
}
}
If lookupData is a nested dictionary :
func buildSource(dataToParse:Set<String>, lookupData:[String:[String:AnyObject]]) {
for item in dataToParse {
guard let dict = lookupData[item] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}
If lookupData is definitely [String:AnyObject] and it's value might be another [String:AnyObject], but it might also not be.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
for item in dataToParse {
guard let dict = lookupData[item] as? [String:AnyObject] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}
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
I want to safely search through values in a swift Dictionary using if lets and making sure it is type safe as I get deeper and deeper into the dictionary. The dictionary contains dictionaries that contains NSArray that contains more dictionary.
At first attempt my code looks like this:
if let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlDict = kkbox["kkbox_dl_url_list"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlArray = kkboxDlUrlDict["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
if let name = dict["name"] as? String {
if name == mediaType.rawValue {
urlStr = dict["url"] as String
}
}
}
} else { return nil }
} else { return nil }
} else { return nil }
How do I shorten it to perhaps one or 2 line?
I realised I can chain it if it is 2 layers. This works:
if let kkboxDlUrlArray = ticket["KKBOX"]?["kkbox_dl_url_list"] as? NSArray {
}
But any chain longer than that, will not compile.
Is there a way to chain through a dictionary more than once?
Thank you
You can chain, but with proper downcast at each step:
if let kkboxDlUrlArray = ((((ticket["KKBOX"] as? Dictionary<String, AnyObject>)?["kkbox_dl_url_list"]) as? Dictionary<String, AnyObject>)?["kkbox_dl_url"]) as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
That doesn't look good though - it's one line, but not readable.
Personally, without using any fancy functional way to do it, I would make the chain more explicit with just one optional binding:
let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject>
let kkboxDlUrlDict = kkbox?["kkbox_dl_url_list"] as? Dictionary<String, AnyObject>
if let kkboxDlUrlArray = kkboxDlUrlDict?["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
In my opinion much easier to read, and with no unneeded indentation
Alternatively, you could use this extension, which mimics the original valueForKeyPath method that used to exist in NSDictionary but got axed for whatever reason:
Swift 4.1
extension Dictionary where Key: ExpressibleByStringLiteral {
func valueFor(keyPath: String) -> Any? {
var keys = keyPath.components(separatedBy: ".")
var val : Any = self
while keys.count > 0 {
if let key = keys[0] as? Key {
keys.remove(at: 0)
if let dic = val as? Dictionary<Key, Value> {
if let leaf = dic[key] {
val = leaf
} else {
return nil
}
} else {
return nil
}
} else {
return nil
}
}
return val
}
Your code would then read:
if let array = ticket.valueFor("KKBOX.kkbox_dl_url_list.kkbox_dl_url") as? [] {
// do something with array
}
Seems like swift 1.2 has added this feature.
"More powerful optional unwrapping with if let — The if let construct can now unwrap multiple optionals at once, as well as include intervening boolean conditions. This lets you express conditional control flow without unnecessary nesting."
https://developer.apple.com/swift/blog/