I have see function to update all value in dictionary credit to Swift replace key value in array of dictionaries with nested dictionaries
but how can I make this update function as part of extension Dictionary? and how can I call the function if I place the function under extension Dictionary?
func update(_ dict: [String: Any], set value: Any, for key: String) -> [String: Any] {
var newDict = dict
for (k, v) in newDict {
if k == key {
newDict[k] = value
} else if let subDict = v as? [String: Any] {
newDict[k] = update(subDict, set: value, for: key)
} else if let subArray = v as? [[String: Any]] {
var newArray = [[String: Any]]()
for item in subArray {
newArray.append(update(item, set: value, for: key))
}
newDict[k] = newArray
}
}
return newDict
}
I did not check if the function itself works, I just transformed it so it is an extension of a [String: Any] dictionary.
It can be used like this : newDictionary = yourDictionary.update(set: yourValue, for: yourKey).
extension [String: Any]
{
func update(set value: Any, for key: String) -> [String: Any]
{
var newDict = self
for (k, v) in newDict {
if k == key {
newDict[k] = value
} else if let subDict = v as? [String: Any] {
newDict[k] = subDict.update(set: value, for: key)
} else if let subArray = v as? [[String: Any]] {
var newArray = [[String: Any]]()
for item in subArray {
newArray.append(item.update(set: value, for: key))
}
newDict[k] = newArray
}
}
return newDict
}
}
To be complete, you could probably also write a function to update your dictionary in place, which would use a bit less memory (untested) :
extension [String: Any]
{
mutating func update(set value: Any, for key: String)
{
if self[key] != nil
{
self[key] = value
}
for (k, v) in self {
if v is [String: Any]
{
var v = v as! [String: Any]
v.update(set: value, for: key)
self[k] = v
}
else if v is [[String: Any]]
{
var newV = [[String: Any]]()
for array in v as! [[String: Any]]
{
var array = array
array.update(set: value, for: key)
newV.append(array)
}
self[k] = newV
}
}
}
}
It can be used like this : yourDictionary.update(set: yourValue, for: yourKey)
Related
The nested dictionary that I am using is build like this
var fDict : [String : Any] = [:]
var errors : [String : Any] = [:]
var error : [String : Any] = [:]
let base : [String : Any] = ["key": "123"]
error["error"] = base
errors["errors"] = error
fDict["profile-response"] = errors
The dictionary is looking like :
{
“profile-response“ : {
“errors” : {
“error” : {
“key“ = “123”
}
}
}
}
I have written code to update the value of key to "abc"
Code :
func replaceErrorWithCustomError( data : inout [String: Any]) {
for (key,value) in data {
if key == "key" {
data.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
replaceErrorWithCustomError(data: &value)
}
}
}
The result before update and after update remains same. Please suggest how to make changes in the current dictionary without taking another dictionary.
You can try this -
func replaceErrorWithCustomError(data: inout [String: Any]) {
func updateError(dict: inout [String: Any]) -> [String: Any] {
for (key, value) in dict {
if key == "key" {
dict.updateValue("abc", forKey: key)
break
} else if var value = value as? [String: Any] {
// This is the key change
// Result must be updated back into parent
dict[key] = updateError(dict: &value)
}
}
return dict
}
updateError(dict: &data)
}
func getCategoryNames() {
Alamofire.request(categoriesUrl).responseJSON { (response) in
if ((response.result.value) != nil) {
var jsonVar = response.result.value as! [String: Any]
if let results = jsonVar["result"] as? [[String: Any]] {
for result in results {
if let names = result["name"] as? String {
var tuy = [""]
tuy.append(names)
I am trying to put those value(names) inside tab(titleNames: tuy)
But it is printing only the last element of the array
Url is
let categoriesUrl = "https://cmsbmnc.agritechie.com/client/categories/"
I need the output like this tuy = ["ABC", "DEF","XYZ"]
let configure = SGPageTitleViewConfigure()
configure.indicatorStyle = .Default
configure.titleAdditionalWidth = 35
self.pageTitleView = SGPageTitleView(frame: CGRect(x: 0, y: pageTitleViewY, width: self.view.frame.size.width, height: 80), delegate: self, titleNames: tuy, configure: configure)
self.view.addSubview(self.pageTitleView!)
In each iteration of the loop you are creating a new tuy array.
You have to create the array once before the loop and declare it as regular empty array
func getCategoryNames() {
Alamofire.request(categoriesUrl).responseJSON { (response) in
if let jsonVar = response.result.value as? [String: Any],
let results = jsonVar["result"] as? [[String: Any]] {
var tuy = [String]()
for result in results {
if let name = result["name"] as? String {
tuy.append(name)
or in a more convenient way
func getCategoryNames() {
Alamofire.request(categoriesUrl).responseJSON { (response) in
if let jsonVar = response.result.value as? [String: Any],
let results = jsonVar["result"] as? [[String: Any]] {
let tuy = results.compactMap { $0["name"] as? String }
It is simple!
Remove that string array var tuy = [""] from
func getCategoryNames() {
Alamofire.request(categoriesUrl).responseJSON { (response) in
if ((response.result.value) != nil) {
var jsonVar = response.result.value as! [String: Any]
if let results = jsonVar["result"] as? [[String: Any]] {
for result in results {
if let names = result["name"] as? String {
var tuy = [""]
tuy.append(names)
and declare it above the function.
You can declare variable of array is outside of function.
var tuy = [String]()
Alamofire.request(categoriesUrl).responseJSON { (response) in
if ((response.result.value) != nil) {
var jsonVar = response.result.value as! [String: Any]
if let results = jsonVar["result"] as? [[String: Any]] {
for result in results {
if let names = result["name"] as? String {
tuy.append(names)
}
}
}
I am trying to simplify the following repetitive code:
var cells = [Cell]()
var modules = [Module]()
var fields = [Field]()
root.child("cells").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let cell = Cell(dict: dict) else { return }
cells.append(cell)
}
root.child("modules").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let module = Module(dict: dict) else { return }
modules.append(module)
}
root.child("fields").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any],
let field = Field(dict: dict) else { return }
fields.append(field)
}
Cell, Module, Field are custom struct. I am thinking whether it's possible to put their initiators init?(dict: [String: Any]) in an array and pass in dict to each of them.
I think you will need to make your structs adopt one protocol for that:
protocol InitializableWithDictionary {
init?(dict: [String : Any])
}
struct Cell: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
struct Module: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
struct Field: InitializableWithDictionary {
//...
init?(dict: [String : Any]){
//...
}
}
var cells: [InitializableWithDictionary] = [Cell]()
var modules: [InitializableWithDictionary] = [Module]()
var fields: [InitializableWithDictionary] = [Field]()
root.child("cells").observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else { return }
let array: [InitializableWithDictionary.Type] = [Cell.self, Module.self, Field.self]
array.forEach {
if let element = $0.init(dict: dict) {
switch element {
case is Cell: cells.append(element)
case is Module: modules.append(element)
case is Field: fields.append(element)
default: break
}
} else { return }
}
}
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
}
}
}
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
}