"Ambiguous reference to member map" when attempting to append/replace array element - swift

Several SO posts like this deal with the same error message, but none of those solutions work. It appears like this could be a case of a misleading error message.
The code below generates an "Ambiguous reference to member map" error for the map call.
Anyone know why?
func saveUser(user: User) {
var userDicts = masterDict["users"] as! [[String:AnyObject]]
let newDict = user.getDict()
// Replace matching element with <newDict>
let replaced = false
masterDict["users"] = userDicts.map {
if ($0["id"] as String! == user.getId()) {
replaced = true
return newDict
} else {
return $0 as [String:AnyObject]
}
}
// If no match found, means must add new user
if (!replaced) {
userDicts.append(newDict)
}
}

Unfortunately, swift is not perfect and can not infer types at all times, this is why you are getting the ambiguous reference. Try userDicts.map {val -> [String:AnyObject] in and swap out $0 for val This will explicitly tell the map that the values coming in are [String:AnyObject], and should be able to return said type

Never isolated the cause of the error so switched to using a for loop, creating a new array inside the loop, and calling masterDict["users"] = newArray after the loop. Not the cleanest, but it fixes the error.

Related

How to get value of a NSSingleObjectArrayI

func responseDataHandler(data: NSDictionary) {
let temperature_c = data.value(forKeyPath: "data.current_condition.temp_C")
DispatchQueue.main.async{
self.Temperature.text = temperature_c as? String
}
}
I have the above code where I am accessing a weather API which returns data in the form of an NSDictionary to this function. I need to access the value in temperature_c which when I try to print it, it says that it is: Optional(<__NSSingleObjectArrayI 0x600002147fd0>(
25
)
). Temperature is the outlet for label on my storyboard which I want to take on the value of 25 however as written now, it doesn't work and I have tried everything to try and access the value in the Single Object Array but nothing is working. I found this stack overflow question that was similar but it doesn't work for my situation because I keep getting the error that temperature_c is of type any and doesn't have subscripts.
The issue is that you can't cast to String an array, you should try to convert it to [String]. So could change your code to:
self.Temperature.text = (temperature_c as? [String])?.first ?? "Not available"
Let's go step by step:
temperature_c as? [String] tries to convert the NSDictionary to a String array which is the expectable type.
Since the previous step may return nil we have to use optional chaining ?. If we got a valid array using first return the the arrays first element.
Since both previous steps can return nil we can use nil coalescing operator to return a default value. In this case I use "Not available" but you can set any value.
You could write it in a more verbose way like this:
var text2Display2 = "Not available"
if let theArray = temperature_c as? [String] {
if let element = theArray.first {
text2Display2 = element
}
}
self.Temperature.text = text2Display2

Value of type _ cannot convert to specified type Set<> Swift

I'm trying to create a set of random exercises. I have made my struct Hashable and Equatable following the tutorial here https://medium.com/#JoyceMatos/hashable-protocols-in-swift-baf0cabeaebd and that is working fine so it's ready to be put in a Set<>.
When I use an Array to collect the workout exercises, as per below, it works fine. But when I switch to a Set<> I get an error "cannot convert value of type [_] to specified type 'Set'. What is it about 'Sets' that mean you can't map in the same way as an Array?
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
//error triggers on the line below if I switch [WorkoutExercise]
//for Set<WorkoutExercise> (which conforms to Hashable/Equatable
let workoutSet : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
print (workoutSet)
}
There is an answer here with a similar error message Cannot convert value of type '[_]' to specified type 'Array' but my array wouldn't be empty as in this example so I don't think this is the same root cause?
UPDATE : for anyone having the same problem, you can use Array but then simply convert the Array to a Set afterwards if the correct elements are Hashable/Equatable
If creating the array works create the array and then make the Set from the array. If all involved objects conform to Hashable this is supposed to work.
func generateWorkout() {
let allPossibleExercises = masterExerciseArray
let numberOfExercisesKey = Int(arc4random_uniform(4)+3)
let workoutArray : [WorkoutExercise] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(allPossibleExercises.count)))
return WorkoutExerciseGenerator( name: allPossibleExercises[randomKey].name,
maxReps: allPossibleExercises[randomKey].maxReps).generate()
}
let workoutSet = Set(workoutArray)
print (workoutSet)
}

How to fetch core data objects into Dictionary in Swift?

I've saved objects in core data, and I am looking how to fetch those objects as a Dictionary
Here is an example of my code where sections is keys for the dictionary and Company as an array of core data objects.
private var companies = Dictionary<String, Array<Company>>()
private var sections: [String] = ["Pending", "Active", "Pending"]
override func viewWillAppear(_ animated: Bool) {
let fetchRequest : NSFetchRequest<Company> = Company.fetchRequest()
let moc = DatabaseController.getContext()
do {
let request = try moc.fetch(fetchRequest)
for case let (index, object) in request.enumerated() {
companies[sections[index]]!.append(object)
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}}
When I am trying to execute my code, I have an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Could anyone help me with that issue?
That error message means that you're force unwrapping an optional that doesn't have a value. In other words you're using ! where you shouldn't. (You should basically never use the force unwrap operator (!).)
Let's look at the line where you do that:
companies[sections[index]]!.append(object)
If we break this down and add the inferred types we have:
let section: String? = sections[index]
let companyArray: Array<Company> = companies[section]!
You're crashing because companies starts off empty, so asking for any of the arrays will return nil. (Actually, I'm not sure how your code is compiling, since you can't subscript into the dictionary with an optional.)
However, if you fix that, we still have a problem because you're using the index of the fetched array to look up the section. If you have more than three companies, that will start to fail.
I suspect you want something like:
for let company in result {
if var companyArray = companies[company.status] {
companyArray.append(company)
} else {
companies[company.status] = [company]
}
}
Where status is an invented property on Company that returns a String like "Pending" or "Active".
I've found the solution, just need to use NSFetchResultController in order to display data in TableView by different sections.

Create empty dictionary

I have a dictionary initialized
var dictionary = [String: [Double]]()
And I want to append
dictionary["Hello"].append(0.0)
but this gives me error "nil".
I tried to solve this by
extension Dictionary {
func appendish(key: String, value: Double) {
if self[key] == nil {
this give me error "Ambiguous reference to member 'subscript'"
}
}
}
How do I solve this? Been stuck at this for hours.
Subscripting a Dictionary with a key returns an optional of type Value?. In your case, dictionary["Hello"] returns a [Double]?. This optionality models the possibility that the dictionary doesn't contain a value for the given key.
If you're only dealing with static data, it's best to just use a literal expression:
let dictionary = [
"Hello": [0.0]
]
If you're using dynamic data, then there are several ways to do what you're trying to achieve, depending on how you would like to handle the nil case:
Use optional chaining
dictionary["Hello"]?.append(0.0)
This appends to the array stored for the key "Hello", but does nothing if there's no such value for that key.
This has the downside of making bugs harder to catch, because the consequence of the silent nil case might not be observed until long after this part of the code has run.
Use force unwrapping
dictionary["Hello"]!.append(0.0)
This appends to the array stored for the key "Hello", but crashes the program if there's no such value for that key.
Unlike optional chaining, this makes it easy to catch the point of failure at runtime. Of course, it comes with the drawback of crashing your program.
Handle the nil case in your own way
if var array = dictionary["Hello"] {
dictionary["Hello"] = nil // This line is a performance optimisation that removes the need for array to be copied
array.append(0.0)
dictionary["Hello"] = array
}
else {
print("No array for the key \"Hello\"") // Handle this as you wish
}
A dictionary look up returns an Optional value because the key might not exist, in which case it returns nil.
If your intention is to append to the array if it exists or create one if there isn't one yet, then the nil coalescing operator ?? comes in handy:
var dict = [String: [Double]]()
dict["hello"] = (dict["hello"] ?? []) + [1]
print(dict) // ["hello": [1.0]]
dict["hello"] = (dict["hello"] ?? []) + [2]
print(dict) // ["hello": [1.0, 2.0]]
This method does create a new array instead of mutating the existing one.
There are a few ways you can do this. Firstly, this is incorrect code:
dictionary["Hello"].append(0.0)
There might not be an array associated with the key "Hello", in which case nil will be returned by the subscript of the dictionary. So you need to unwrap it, either forced or un-forced:
dictionary["Hello"]?.append(0.0)
// or
dictionary["Hello"]!.append(0.0)
But I think what you really want to do is
if dictionary["Hello"] != nil {
dictionary["Hello"]!.append(0.0)
} else {
dictionary["Hello"] = [0.0]
}
After a long time of fiddling around with extensions and stuff (I am not familiar with this area of swift), I finally wrote the method appendish method that you were intended to write:
extension Dictionary where Value : RangeReplaceableCollection & ExpressibleByArrayLiteral, Value.Iterator.Element == Value.Element {
mutating func appendish(key: Key, value: Value.Element) {
if self[key] != nil {
self[key]!.append(value)
} else {
self[key] = [value]
}
}
}
// test
var dict = [String: [Double]]()
dict.appendish(key: "Hello", value: 0.0)

How to check if a variable is nil

I have a variable
var a: [AnyObject? -> Void]
and I am adding data in to it by append method. Now I want to check if the variable is nil or not. I tried using [] but not working and also tried "", this also not working, can anyone tell what is the meaning of this variable and how to check if it is nil.
As far as I understand, var a is an Array of functions that take an optional Object of any type, and return void. So these functions's parameter IS optional, but the Array itself isn't : it cannot be nil, or it would be declared [AnyObject? -> Void]? , no?
EDIT : if, nevertheless, you declared this a as an optional (but WHY would you do that ?) - adding a ? - you check an optional existence with if let :
if let b = a {
// a not nil, do some stuff
} else {
// a is null
}
If you just want to check if the array is empty, use isEmpty method from Swift Array
Update: Xcode 7.3 Swift 2.2
If you want to check if a variable is nil you should use if let to unwrap if for you. There is no need to create a second var.
let str = "123"
var a = Int(str)
if let a = a {
print(a)
}
Or
if let a = Int(str) {
print(a)
}
In Swift, nil is not a pointer—it is the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.
So, You can check it with below code:
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
if convertedNumber != nil {
println("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."
Please refer this about nil for more information.
In Swift 3.0
if let imageURL = dictObj["list_image"] as? String {
print(imageURL)
}
You can use if let. if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value.
var a:Int=0
if let b=a{
println(a)
} else {
println("Value - nil")
}
But for Strings you can also use .isEmpty() If you have initialized it to "".
var str:String=""
if !str.isEmpty(){
println(str)
}
For me none of the above solutions worked when I was using an AVFoundation object.
I would get Type 'AVCaptureDeviceInput does not conform to protocol 'BooleanType' when I tried if (audioDeviceInput) and I would get Binary operator '!=' cannot be applied to operands of type 'AVCaptureDeviceInput' and 'nil'.
Solution in my situation
if (audioDeviceInput.isEqual(nil))
nil is a pointer like any other and can be referenced as such, which is why this works.