How to add another dictionary entry to an existing dictionary to form a new dictionary (i.e. not append) - swift

I want to add another dictionary entry to a dictionary in swift e.g.
let a: [String: Any] = ["Test": 1, "good":false]
let b = a + ["hello": "there"]
print(b)
(Sorry if + looks crazy here, as that's how Kotlin achieves this. I'm more familiar with Kotlin than Swift.)
But I get the error Binary operator '+' cannot be applied to two '[String : Any]' operands
I can't use updateValue too
let a: [String: Any] = ["Test": 1, "good":false]
let b = a.updateValue("hello": "there")
print(b)
It will error stating Cannot use mutating member on immutable value: 'a' is a 'let' constant
I can do it by an extension function as per proposed https://stackoverflow.com/a/26728685/3286489
but it looks overkill.
let b = a.merge(dict: ["hello": "there"])
print(b)
extension Dictionary {
func merge(dict: Dictionary<Key,Value>) -> Dictionary<Key,Value> {
var mutableCopy = self
for (key, value) in dict {
// If both dictionaries have a value for same key, the value of the other dictionary is used.
mutableCopy[key] = value
}
return mutableCopy
}
}
Is there a simple operator I can just add another entry to it to for a new dictionary?
Note: I'm not referring to append as per How to append elements into a dictionary in Swift?, as I receive an immutable dictionary let that I need to add another entry to it.

There is a built-in merging(_:uniquingKeysWith:) function on Dictionary that does exactly what you need.
let dictionary = [1:1]
let otherDictionary = [1:2, 2:3]
// This will take the values from `otherDictionary` if the same key exists in both
// You can use `$0` if you want to take the value from `dictionary` instead
let mergedDict = dictionary.merging(otherDictionary, uniquingKeysWith: { $1 })
If you want, you can easily define a + operator for Dictionary that uses the above function.
extension Dictionary {
static func + (lhs: Self, rhs: Self) -> Self {
lhs.merging(rhs, uniquingKeysWith: { $1 })
}
}
let addedDict = dictionary + otherDictionary

Related

Use of undeclared type 'valueMirror' when using Mirror

I am trying to map a struct to other class that have same properties. but it keep showing this error
Use of undeclared type 'valueMirror'
My code
extension Mapper {
func map<T:Object>(to type: T.Type){
let object = T()
let m = Mirror(reflecting: self)
for property in m.children {
guard let key = property.label else { continue }
let value = property.value
let valueMirror = Mirror(reflecting: value)
if valueMirror.displayStyle == .collection {
let array = value as! valueMirror.subjectType // <-- error
object.setValue(array.asRealMList, forKey: key)
} else {
object.setValue(value, forKey: key)
}
}
}
}
valueMirror.subjectType is not a type as far as the compiler is concerned. There must be a compile time type after as!.
Since the only place you are using array is in array.asRealMList, you probably just need to cast value to a type that has the property asRealMList. As you said in the comments, this is an extension on Array.
Luckily Array is covariant, so even without knowing which type of array it is, you'll be able to cast any array to [Any]:
let array = value as! [Any]
valueMirror.subjectType is of type Any.Type.
You probably want to cast value to Any.

Sort Dictionary Alphabetically Cannot assign value of type '[(key: String, value: AnyObject)]' to type 'Dictionary<String, AnyObject>?'

I have this Dictionary, which I am getting from a web service:
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! Dictionary<String, AnyObject>
and now I am trying to sort them alphabetically like so:
self.appDelegate.communityArray = json.sorted(by: {$0.0 < $1.0})
But I get this error:
Cannot assign value of type '[(key: String, value: AnyObject)]' to
type 'Dictionary?'
What am I doing wrong?
This is how I am defining communityArray:
var communityArray: Dictionary<String, AnyObject>?
As mentioned in the comments a dictionary – a collection type containing key-value pairs – is unordered by definition, it cannot be sorted.
The sorted function of collection applied to a dictionary treats the dictionary for example
["foo" : 1, "bar" : false]
as an array of tuples
[(key : "foo", value : 1), (key : "bar", value : false)]
and sorts them by key (.0) or value (.1) (I suspect sorting by value Any will raise a compiler error)
The error occurs because you cannot assign an array of tuples to a variable declared as dictionary. That's almost the literal error message.
As a compromise I recommend to map the dictionary to a custom struct (similar to a tuple but better to handle)
struct Community {
let key : String
let value : Any
}
Your variable name already implies that you want a real array
var communityArray = [Community]()
let json = try JSONSerialization.jsonObject(with: data!) as! Dictionary<String, Any>
communityArray = json.map { Community(key: $0.0, value: $0.1 }.sorted { $0.key < $1.key }
sorted(by:) Returns the elements of the sequence, sorted using the given predicate as the comparison between elements, so you can not use as a dictionary.
For more info about this function you may read sorted(by:) documentation

Check if variable is computed or stored

In my app I translate objects from custom classes into dictionaries so that they can be saved locally in a plist as well as on a server. I use the following to turn the properties of a class into a dictionary:
func dictionary() -> [String : Any] {
var count: UInt32 = 0;
let myClass: AnyClass = self.classForCoder;
let properties = class_copyPropertyList(myClass, &count);
var dictionaryRepresentation: [String:Any] = [:]
for i in 0..<count {
let property = properties![Int(i)]
let cStringKey = property_getName(property);
let key = String(cString: cStringKey!)
dictionaryRepresentation[key] = self.value(forKey: key) as Any
}
return dictionaryRepresentation
}
I have a problem, however, with computed properties. It seems that those are computed and the returned value gets put into the dictionary as well, which I would like to avoid. So here is my question:
Is it possible to check whether is a property computed programatically using only its name?
I am assuming this could be possible by trying to assign a value to it which would give me an error or some similar approach.
Here is what seems to be a working solution, based on suggestion by dasblinkenlight.
Rather than using the Objective-C method outlined above, create a Mirror of the class which has a children made up of all settable properties, therefore excluding computables.
Used like this:
let mirror = Mirror(reflecting: MyObject)
for case let (label?, value) in mirror.children {
print (label, value)
}
Here label is the name of the variable and value is obviously the value.
EDIT: In case anyone wants to convert objects into dictionary, I am posting the full code here as well. Do however remember that if values are custom objects as well, those will need to be converted too.
func dictionary() -> [String:Any] {
let mirror = Mirror(reflecting: self)
var dictionaryRepresentation = [String:Any]()
for case let (label, value) in mirror.children {
guard let key = label else { continue }
dictionaryRepresentation[key] = value
}
return dictionaryRepresentation
}
You can try property_copyAttributeList(_:_:) function, it may contain a read-only marker for swift's computed properties. Although I guess let properties also will have that marker, so you must find a way to differ them.

Swift Dictionary access value through subscript throws Error

Accessing a Swift Dictionary value through subscript syntax is gives error Ambiguous reference to member 'subscript'
Here is the code
class Model {
struct Keys {
static let type :String = "type" //RowType
static let details :String = "details"
}
var type :RowType = .None
var details :[Detail] = []
init(with dictionary:Dictionary<String, Any>) {
if let type = dictionary[Keys.type] as? String {
self.type = self.rowTypeFromString(type: type)
}
if let detailsObj = dictionary[Keys.details] as? Array { //Error : Ambiguous reference to member 'subscript'
}
}
}
if i remove the type casting as? Array at the end of optional-binding it compiles fine
I am expecting the value of details key to be an Array, I know that i can use [String,Any] instead of Dictionary<Key, Value>, What is causing the issue ?
Array doesn't work like NSArray, you explicitly need to tell what type your array is storing.
If you want to store Detail objects in it, the proper syntax is Array<Detail> or simply [Detail]
if let detailsObj = dictionary[Keys.details] as? Array<Detail> {
}
Issue is solved by explicitly specifying the Type of objects the array contains, In my case it was Array<[String:Any]>
if let detailsObj = dictionary[Keys.details] as? Array<[String:Any]> { //we should also specify what type is present inside Array
}
Credits: #Hamish, #Dávid Pásztor
Thanks!

Check whethere an array contain element

I wonder why following not working:
var tmp = [AnyObject] ()
for (key, value) in dictionary{
if (tmp.contains(value)){
}
tmp.append(value as AnyObject)
}
Look like there is no method .contain for an Array. it force me to use some other method tmp.contains(where: <#T##(AnyObject) throws -> Bool#>)
But i dont want to. I simply want to check whether an array contain that specific object!
I recommend switching from AnyObject to AnyHashable(of course if that's possible), because AnyHashable is Equatable. Your code would look like this:
var tmp = [AnyHashable]()
var dictionary: [Int: Any] = [1: "Hello1", 2: "Hello2", 3: "Hello3", 4: false, 5: 289 ]
for (key, value) in dictionary{
if tmp.contains(value as! AnyHashable){
continue
}
tmp.append(value as! AnyHashable)
}
Hope it helps!
That's because AnyObject is not an Equatable object. If you have an AnyObject array, you have to do the comparisons yourself. If you used an array of Strings or a custom class for instance you could use this contains() method of Sequence