flatMap and `Ambiguous reference to member` error - swift3

Consider the following code:
typealias PersonRecord = [String : AnyObject]
struct Person {
let name: String
let age: Int
public init(name: String, age: Int) {
self.name = name
self.age = age
}
}
extension Person {
init?(record : PersonRecord) {
guard let name = record["name"] as? String,
let age = record["age"] as? Int else {
return nil
}
self.name = name
self.age = age
}
}
Now I want to create an array of Persons from an array of Records:
let records = // load file from bundle
let persons = records.flatMap(Person.init)
But I get the following error:
error: ambiguous reference to member 'init(name:age:)'
If I move the failable initializer out of the extension, I still get the same error.
What am I missing here, is this not a correct usage of flatMap?
EDIT - solved:
Found the error: the code that read in the records file from disk, returned the wrong type. Once I fixed that, the error went away.

For this to work
let persons = records.flatMap(Person.init)
the param passed to the flatMap closure must be the same type received by Person.init, so it must be PersonRecord.
Then records must be a list of PersonRecord, something like this
let records: [PersonRecord] = []
Now it works
let persons = records.flatMap(Person.init)

Most of the times the Ambiguous reference to member in a map or flatMap is because the return type isn't specified
array.flatMap({ (item) -> ObjectToReturn? in })

Related

Swift: Finding an Object Property via regex

Target: The following function shall iterate over an array of objects and check a specific property of all objects. This property is a string and shall be matched with a user input via regex. If there's a match the object shall be added to an array which will further be passed to another function.
Problem: I don't know how to set up regex in Swift 3. I'm rather new in Swift at all, so an easily understandable solution would be very helpful :)
How it currently looks like:
func searchItems() -> [Item] {
var matches: [Item] = []
if let input = readLine() {
for item in Storage.storage.items { //items is a list of objects
if let query = //regex with query and item.name goes here {
matches.append(item)
}
}
return matches
} else {
print("Please type in what you're looking for.")
return searchItems()
}
}
This is what Item looks like (snippet):
class Item: CustomStringConvertible {
var name: String = ""
var amount: Int = 0
var price: Float = 0.00
var tags: [String] = []
var description: String {
if self.amount > 0 {
return "\(self.name) (\(self.amount) pcs. in storage) - \(price) €"
} else {
return "\(self.name) (SOLD OUT!!!) - \(price) €"
}
}
init(name: String, price: Float, amount: Int = 0) {
self.name = name
self.price = price
self.amount = amount
}
}
extension Item: Equatable {
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.name == rhs.name
}
}
Solved. I just edited this post to get a badge :D
For the purpose of letting the answer to be generic and clear, I will assume that the Item model is:
struct Item {
var email = ""
}
Consider that the output should be a filtered array of items that contains items with only valid email.
For such a functionality, you should use NSRegularExpression:
The NSRegularExpression class is used to represent and apply regular
expressions to Unicode strings. An instance of this class is an
immutable representation of a compiled regular expression pattern and
various option flags.
According to the following function:
func isMatches(_ regex: String, _ string: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: regex)
let matches = regex.matches(in: string, range: NSRange(location: 0, length: string.characters.count))
return matches.count != 0
} catch {
print("Something went wrong! Error: \(error.localizedDescription)")
}
return false
}
You can decide if the given string does matches the given regex.
Back to the example, consider that you have the following array of Item Model:
let items = [Item(email: "invalid email"),
Item(email: "email#email.com"),
Item(email: "Hello!"),
Item(email: "example#example.net")]
You can get the filtered array by using filter(_:) method:
Returns an array containing, in order, the elements of the sequence
that satisfy the given predicate.
as follows:
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailItems = items.filter {
isMatches(emailRegex, $0.email)
}
print(emailItems) // [Item(email: "email#email.com"), Item(email: "example#example.net")]
Hope this helped.
You can do the same with filter function
let matches = Storage.storage.items.filter({ $0.yourStringPropertyHere == input })

How to get two data from FMDB and return a String combined two of them in swift?

I have two records in my database, 'FirstName and LastName and I wanna get two of them then return a FullName, showing FullNames in tableView. Using Swift3 and Xcode8.2.1, FMDB
here's the func of getFullName
func getFullName() -> String{
StudentDataBase.getInstance()
sharedInstance.database!.open()
let result : FMResultSet! = sharedInstance.database!.executeQuery("SELECT FirstName, LastName FROM Student_info", withArgumentsIn: nil)
let fullName = (result?.string(forColumn: "FirstName"))! + " " + (result?.string(forColumn: "LastName"))!
return fullName
}
sharedInstance is a global one, StudentDataBase is s singleton class:
class StudentDataBase : NSObject {
var database: FMDatabase? = nil
var pathToDB: String!
class func getInstance() -> StudentDataBase{
if((sharedInstance.database) == nil)
{
sharedInstance.database = FMDatabase(path: Utility.getPath("data.db"))
}
return sharedInstance
}
and my Student.swift goes like this:
import UIKit
//the model class to fetch the data from data.db
class Student : NSObject {
var studentID: String = String()
var fstName: String = String()
var lstName: String = String()
var phoneNum: String = String()
}
I got a thread goes like this enter image description here
Thank you so much if anyone can help
After editing the Query into
let result : FMResultSet! = sharedInstance.database!.executeQuery("SELECT ifnull(FirstName,'') as FirstName, ifnull(LastName,'') as LastName FROM Student_info", withArgumentsIn: nil)
There's another error in combining them into one String:
enter image description here
You are getting this crash because either you are getting nil for FirstName or LastName, and you can use ifnull with your select query and return empty string if it is nil. So change your query like this way.
let result : FMResultSet! = sharedInstance.database!.executeQuery("SELECT ifnull(FirstName,'') as FirstName, ifnull(LastName,'') as LastName FROM Student_info", withArgumentsIn: nil)
Note: You haven't added WHERE clause with query so that this will give you all the records form Student_info table also it is batter if you check result.next() to confirm result contains records or not.

Getting ambiguous reference to member subscript

I am updating my code to swift3.0 but getting ambiguous refrence to member? What wrong i might be doing. Here is the method I am getting error in.
open class func parseJsonTenantList(_ list: [NSDictionary]?, strElementName: String, attrName1: String, attrNameValue2: String) -> [TenantRegister]
{
var renantList: [TenantRegister] = []
var key: String?
if let dict : [NSDictionary] = list {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1: AnyObject = dict[i].value(forKey: attrName1)
{
key = s1 as? String
}
if let s2: AnyObject = dict[i].value(forKey: attrNameValue2)
{
value = s2 as? String
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
return renantList
}
The issue is you are using NSDictionary, to solved your problem simply cast the list to Swift's native type [[String:Any]] and then use subscript with it instead of value(forKey:)
if let dict = list as? [[String:Any]] {
var value: String?
for i in 0..<dict.count {
/// if attribute name doesn't match then it returns nil
if let s1 = dict[i][attrName1] as? String
{
key = s1
}
if let s2 = dict[i][attrNameValue2] as? String
{
value = s2
}
if (!(String.stringIsNilOrEmpty(value) && String.stringIsNilOrEmpty(key)))
{
let t: TenantRegister = TenantRegister()
t.name = key
t.tenantId = Guid(value!)
renantList.append(t)
}
}
}
In Swift use native type Dictionary [:] and Array [] instead of NSDictionary and NSArray to overcome this type of issues.

How to fix Cannot convert value of type ... to expected argument type inout _

I have this simple Swift3 code where if complains about [Channel]:
var channels = [Channel]()
....
for (_, json) in json["entities"] {
let channel = Channel(json: json)
self.channels += [channel]
^ Cannot convert value of type [Channel] to expected argument type inout _
}
This is the channel class:
class Channel {
var uuid: String
var title: String?
var isPublic: Bool
init(uuid: String) {
self.uuid = uuid
self.title = ""
self.isPublic = false
}
init?(json: JSON) {
self.uuid = json["uuid"].stringValue
self.title = json["title"].stringValue
self.isPublic = json["public"].boolValue
}
}
Some postings indicate the message may be related to closures but I can't see a closure here.
How to fix this error in a simple for loop?
The error is coming from the += statement. Channel(json:) is a failable initializer (note the init?) and it returns an Optional which must be unwrapped. So you are trying to apply += to [Channel] and [Channel?] and the types are not compatible. The error message is less than clear, because of the way += is defined.
public func +=<C : Collection>(lhs: inout [C.Iterator.Element], rhs: C)
Swift is unable to reconcile the type of rhs which is [Channel?] with the type of lhs which is [Channel].
The fix is to unwrap the Channel? returned by the failable initializer Channel(json:):
var channels = [Channel]()
....
for (_, json) in json["entities"] {
if let channel = Channel(json: json) {
self.channels += [channel]
}
}
#andig. [1] your example cannot be compiled because both json and JSON are not defined. [2] Now let's assume you made a failable initializer init?(json: JSON){} correctly within Class Channel then vacawama's explanation is correct. Changing your code to the below should work.
for (_, json) in json["entities"] {
if let channel = Channel(json: json) {
self.channels += [channel!]
}
}

How do I cast an Array<AnyObject> to an empty array variable?

Problem: I can't assign a string array (typed as 'AnyObject') to an empty array.
Steps:
1. Get a dictionary containing strings & arrays.
2. Grab an array from this dictionary via the key 'photos'.
Note: Xcode's warning suggest that I give the explicit type 'AnyObject'.
3. Create an empty array.
4. Attempt to assign it (failed).
let responseDictionary = responseDict as [String : AnyObject]
let ricPhotos:AnyObject = responseDictionary["photos"]!
var thePhotos:Array<AnyObject>?
thePhotos = ricPhotos <--- fails
Compiler Error:
...'AnyObject' is not convertible to 'Array<AnyObject>'
Question: How do I assign 'ricPhotos' to the empty array 'thePhotos' and preferably cast 'AnyObject' to 'String'?
Revision
let responseDictionary = responseDict as [String : AnyObject]
var anyObject: AnyObject? = responseDictionary["photos"]
Okay, 'anyObject' appears to be a Dictionary, and inside it is 'photo' which is an array; as seen in the data.
Here's some of the data(anyObject):
{
page = 1;
pages = 1334;
perpage = 100;
photo = (
{
farm = 3;
"height_m" = 336;
"height_s" = 161;
"height_sq" = 75;
"height_t" = 67;
id = 15166756998;
isfamily = 0;
isfriend = 0;
ispublic = 1;
owner = "127012961#N08";
secret = 053032f300;
server = 2941;
title = "You #NYPL";
"url_m" = "https://farm3.staticflickr.com/2941/15166756998_053032f300.jpg";
"url_s" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_m.jpg";
"url_sq" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_s.jpg";
"url_t" = "https://farm3.staticflickr.com/2941/15166756998_053032f300_t.jpg";
"width_m" = 500;
"width_s" = 240;
"width_sq" = 75;
"width_t" = 100;
},
...etc.
I want to grab the 'photo' array. But I can't downcast 'anyObject' to 'Dictionary' so that I can subscript it. I tried:
var anyObject:Dictionary = responseDictionary["photos"]
But I got:
'(String, AnyObject)' is not convertible to '[String : AnyObject]'
So I'm stuck with:
var anyObject: AnyObject? = responseDictionary["photos"]
So with anyObject, I tried to access 'photos':
let RicPhotos = anyObject["Photo"] as [String : AnyObject]
...I also tried:
let RicPhotos = anyObject["Photo"] as Array<Dictionary<String,String>>
But I got:
'AnyObject?' does not have a member named 'subscript'
I can see the data, but I can't extract into an empty variable.
I attempted to downcast to a specific type (Dictionary) but the compiler refuses.
There must be a strait forward way of getting an embedded array from a dictionary whilst casting to its respective cast (without the 'anyObject').Any ideas?
Edited my answer to fit your edit...
let responseDictionary = [:] as [String : AnyObject]
var photosDic: AnyObject? = responseDictionary["photos"]
if let photosDic = photosDic as? Dictionary<String, AnyObject> {
var photosArray: AnyObject? = photosDic["photo"]
if let photosArray = photosArray as? Array<Dictionary<String, AnyObject>> {
//There you go
}
}