How to combine arrays depending on user selection? - ios

I want to see what user selected like the name of book and its assocaited chapters
I did this
struct bookChpt {
var book:[String] = []
var chapter:[[Int]] = []
}
let chptSelected = [bookChpt(book:bookArr,chapter:chptArr)]
var bookArr:[String] = []
var chptArr:[[Int]] = []
I have this in viewDidLoad()
if let bTitle = result.value(forKey: "bookTitle") as? String
{
bookArr.append(bTitle)
}
if let cNo = result.value(forKey: "chpNo") as? [Int]
{
chptArr.append(cNO)
}
print(chptSelected)
I am getting this
bookChpt( book: ["Hobbit", "LOTR"], chapter: [[3,5],4])
but I like to see this
["Hobbit", 3, 5], ["LOTR", 4]

There are a couple of possibilities. You could add a function to the struct to display its contents in the way you want:
struct BookChapter {
var book:[String] = []
var chapter:[[Int]] = []
func display() -> [[Any]] {
var output = [[Any]]()
for i in 0..<book.count {
output.append([book[i], chapter[i]])
}
return output
}
}
Or you could modify the structure of the struct to contain the book and chapters as tuples:
struct BookChapter {
var book:[(String, [Int])]
}
Going a bit further, anywhere you see a loop - such as in the display function above - you might also consider using map to achieve the same thing:
func display() -> Any {
return book.enumerated().map { $0.element + " " + chapter[$0.offset].description }
}

If you use an Dictionary like this, you can print the key and value whatever way you wanted.
var bookChapters = [String: [Int]]()
bookChapters["Hobbit"] = [1,2,3]
bookChapters["Hobbit"]?.append(contentsOf: [4])
for (book, chapter) in bookChapters {
print("\(book): \(chapter)")
}

Change your struct to
struct BookChapt {
var book: String = ""
var chapter: [Int] = []
}
and in viewDidLoad()
var bookName = ""
var chapters:[Int] = []
if let bTitle = result.value(forKey: "bookTitle") as? String
{
bookName = bTitle
}
if let cNo = result.value(forKey: "chpNo") as? [Int]
{
chapters = cNo
}
let chptSelected = BookChapt(book: bookName, chapter: chapters)
print(chptSelected)

Related

How to get the struct value from another class?

I want to fetch the offerApplied value from the struct in another class. Here is the struct block:
struct Offer: JsonDeserilizer {
var offerDesC:String = ""
var discount:Double = 0.0
var offerId:String = ""
var offerCode:String = ""
var offerApplied:Int = 0
mutating func deserilize(values: Dictionary<String, Any>?) {
self.offerDesC = values?["offer_desc"] as? String ?? ""
self.discount = values?["discount"] as? Double ?? 0.0
self.offerId = values?["_id"] as? String ?? ""
self.offerCode = values?["offer_code"] as? String ?? ""
self.offerApplied = values?["is_applied"] as? Int ?? 0
}}
And an explanation will be very helpful.
You can create getter function in your struct, that can return specific value as you want. In target class initialise your struct properties (by calling deserilize(), or however you wish) and call the getter function to fetch values.
Update-:
As #Joakim mentioned, your properties are not private, so there is no need to create getter function. You could infact directly refer the property from created object.
struct Offer {
var offerDesC:String = ""
var discount:Double = 0.0
var offerId:String = ""
var offerCode:String = ""
var offerApplied:Int = 0
mutating func deserilize() {
self.offerDesC = "xy"
self.discount = 20
self.offerId = "okkk"
self.offerCode = "12"
self.offerApplied = 245
}
// func returnOfferApplied() -> Int{
// return offerApplied
// }
}
class xyz{
var obj = Offer()
func printOffer(){
obj.deserilize()
print(obj.offerApplied)
}
}
let obj = xyz()
obj.printOffer()
First create struct class at global file.
Look in my example
struct ColorPalette {
static let UEMColor = hexStringToUIColor(hex: "72279C")
static let uemIconPrimary = hexStringToUIColor(hex: "282D68")
}
Access Struct Like this:
ColorPalette.uemIconPrimary

How to populate an object array using a Firebase Database

I'm unable to create a function which returns an array of Product objects from my Firebase database.
I attempted using a switch within the closure to set variables to their respective names, and then instantiate the object after all variables have been set. I tried to play around with the scope of the closure to try and access the "retrieved value". Below is a snapshot of my Firebase Database, my product object, and my function.
func createArray() -> [Product] {
var newArray = [Product]()
let ref = Database.database().reference()
var productName = String()
var productHealth = String()
var productPrice = 0
var productImage = String()
for bigIndex in 0..<7 {
for smallIndex in 0..<4{
ref.child("masterSheet/\(bigIndex)/\(smallIndex)").observeSingleEvent(of: .value) { (snapshot) in let retrievedValue = snapshot.value}
//I used to have a switch statement here that went like
//switch smallIndex{ case 0: productPrice = retrievedValue as! String }
}
let completeProduct = Product(productName: productName, productHealth: productHealth, productPrice: productPrice, productImage: productImage)
newArray.append(completeProduct)
}
return newArray
}
Product Object:
import Foundation
import UIKit
class Product {
var productName: String
var productHealth: String
var productPrice: Int
var productImage: String
init(productName: String, productHealth: String, productPrice: Int, productImage: String ){
self.productName = productName
self.productHealth = productHealth
self.productPrice = productPrice
self.productImage = productImage
}
}
My goal is to produce an array of all of the items in the Database.
You need to fetch the value from smallIndex and set it into modelClass Product like this:
func createArray() -> [Product] {
var newArray = [Product]()
let ref = Database.database().reference()
for bigIndex in 0..<7 {
for smallIndex in 0..<4{
ref.child("masterSheet/\(bigIndex)/\(smallIndex)").observeSingleEvent(of: .value) { (snapshot) in let retrievedValue = snapshot.value}
//I used to have a switch statement here that went like
//switch smallIndex{ case 0: productPrice = retrievedValue as! String
let singleProduct = Product()
singleProduct.productName = "fetch value from small index"
singleProduct.productPrice = retrievedValue
sigleProduct.productHealth = "fetch value from small index"
newArray.append(singleProduct)
}
}
}
return newArray
}
For anyone wondering, this is the solution that ended up working for me :)
var product: [Product] = []
ref = Database.database().reference()
databaseHandle = ref.child("masterSheet").observe(.value) { (snapshot) in
guard let rawData = snapshot.value as? [AnyObject] else { return }
for item in rawData {
guard let itemArray = item as? [AnyObject] else { continue }
var pro = Product()
if itemArray.count > 0 {
pro.productName = itemArray[0] as? String
}
if itemArray.count > 1 {
pro.productHealth = itemArray[1] as? String
}
if itemArray.count > 2 {
pro.productPrice = itemArray[2] as? Int
}
if itemArray.count > 3 {
pro.productImage = itemArray[3] as? String
}
// if you use dict, do like this:
//pro.productImage = itemArray["productImage"] as? String
//pro.productPrice = itemArray["productPrice"] as? Int
product.append(pro)
}
self.tableView.reloadData()
}

Adding an array of Json Data to Realm

I'm making an app for airports and I'm getting an array of data from one api, like so:
"data":[
{"id":"001","code":"ABZ","name":"Aberdeen","country":"United Kingdom"},
{"id":"002","code":"AUH","name":"Abu Dhabi","country":"United Arab Emirates"},
.
.
.
]
AND :
"airports":[
{"from":"001",
"to":["1","3","11","13","12","20","23","27","29","31","33"]
},
.
.
.
]
I have created realm model classes:
class AirportsDataRealm: Object {
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
#objc dynamic var code: String = ""
#objc dynamic var country: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
class AirportsFromToRealm: Object {
#objc dynamic var fromID: Int = 0
var toID = List<Int>()
override static func primaryKey() -> String? {
return "fromID"
}
}
now I want to save it into realm, I'm using swiftyJSON and I have used for-loop to do it and it is working fine but I think it's taking long time since the array is very long, here is what I've done:
// Airports Data
let countData = json["data"].count
for i in 0...countData - 1{
let airportsDataModel = AirportsDataRealm()
airportsDataModel.code = json["data"][i]["code"].stringValue
airportsDataModel.name = json["data"][i]["name"].stringValue
airportsDataModel.country = json["data"][i]["country"].stringValue
airportsDataModel.id = Int(json["data"][i]["id"].stringValue)!
try! realm.write {
realm.add(airportsDataModel, update: true)
}
}
//Airports FROM-TO
let countFromTo = json["airports"].count
for i in 0...countFromTo - 1{
let fromToDataModel = AirportsFromToRealm()
fromToDataModel.fromID = Int(json["airports"][i]["from"].stringValue)!
let arrayTo = json["airports"][i]["to"].arrayValue.map{ $0.intValue }
fromToDataModel.toID.append(objectsIn: arrayTo)
try! realm.write {
realm.add(fromToDataModel, update: true)
}
}
is there any way to save the whole array in realm in one shot without for-loop?
P.S
"there should be a relation between the two tables because each from 'id' has a list of 'to' id's and the id's are from the data table, for now I managed to create this relations when fetching the data using filters ,, so just ignore this"
Thank you
Simply use map method,
First I needed to add initializers to my object classes and pass json array as a parameter, like so:
class AirportsDataRealm: Object {
#objc dynamic var name: String = ""
#objc dynamic var id: Int = 0
#objc dynamic var code: String = ""
#objc dynamic var country: String = ""
convenience required init(withJSON json : JSON) {
self.init()
self.name = json["name"].stringValue
self.id = json["id"].intValue
self.code = json["code"].stringValue
self.country = json["country"].stringValue
}
override static func primaryKey() -> String? {
return "id"
}
}
class AirportsFromToRealm: Object {
#objc dynamic var fromID: Int = 0
var toID = List<Int>()
convenience required init(withJSON json : JSON) {
self.init()
self.fromID = json["from"].intValue
let toArray = json["to"].arrayValue.map{ $0.intValue }
self.toID.append(objectsIn: toArray)
}
override static func primaryKey() -> String? {
return "fromID"
}
}
Then by using map method the code will look like this:
func updateAirport(json: JSON) {
// Airports Data
let airportsData : [AirportsDataRealm]
let airportsDataJsonArray = json["data"].array
airportsData = airportsDataJsonArray!.map{AirportsDataRealm(withJSON: $0)}
//Airports FROM-TO
let airportsFromTo : [AirportsFromToRealm]
let airportsFromToJsonArray = json["airports"].array
airportsFromTo = airportsFromToJsonArray!.map{AirportsFromToRealm(withJSON: $0)}
//Write To Realm
try! realm.write {
realm.add(airportsData, update: true)
realm.add(airportsFromTo, update: true)
}
}
No for loops anymore ^_^

Merge duplicate CNCONTACT array swift 3 contacts framework

I did find duplicate contacts list from this method , now i'm stuck in merging the duplicates, any idea how i can do this.
I fetched duplicate using this code Referenced from previous question.
let formatter = CNContactFormatter()
formatter.style = .fullName
let keys = [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]
let request = CNContactFetchRequest(keysToFetch: keys)
var contactsByName = [String: [CNContact]]()
try! self.store.enumerateContacts(with: request) { contact, stop in
guard let name = formatter.string(from: contact) else { return }
contactsByName[name] = (contactsByName[name] ?? []) + [contact] // or in Swift 4, `contactsByName[name, default: []].append(contact)`
}
let duplicates = contactsByName.filter { $1.count > 1 }
If you followed my previous answer for fetching duplicates list after you can use this code to merge duplicates.
func mergeAllDuplicates() -> CNContact {
let duplicates: [Array<CNContact>] = //Array of Duplicates Contacts
for item in duplicates {
// CNCONTACT PROPERTIES
var namePrefix: [String] = [String]()
var givenName: [String] = [String]()
var middleName: [String] = [String]()
var familyName: [String] = [String]()
var previousFamilyName: [String] = [String]()
var nameSuffix: [String] = [String]()
var nickname: [String] = [String]()
var organizationName: [String] = [String]()
var departmentName: [String] = [String]()
var jobTitle: [String] = [String]()
var phoneNumbers: [CNPhoneNumber] = [CNPhoneNumber]()
var emailAddresses: [NSString] = [NSString]()
var postalAddresses: [CNPostalAddress] = [CNPostalAddress]()
var urlAddresses: [NSString] = [NSString]()
var contactRelations: [CNContactRelation] = [CNContactRelation]()
var socialProfiles: [CNSocialProfile] = [CNSocialProfile]()
var instantMessageAddresses: [CNInstantMessageAddress] = [CNInstantMessageAddress]()
// Filter
for items in item {
namePrefix.append(items.namePrefix)
givenName.append(items.givenName)
middleName.append(items.middleName)
familyName.append(items.familyName)
previousFamilyName.append(items.previousFamilyName)
nameSuffix.append(items.nameSuffix)
nickname.append(items.nickname)
organizationName.append(items.organizationName)
departmentName.append(items.departmentName)
jobTitle.append(items.jobTitle)
for number in items.phoneNumbers {
phoneNumbers.append(number.value)
}
for email in items.emailAddresses {
emailAddresses.append(email.value)
}
for postal in items.postalAddresses {
postalAddresses.append(postal.value)
}
for url in items.urlAddresses {
urlAddresses.append(url.value)
}
for relation in items.contactRelations {
contactRelations.append(relation.value)
}
for social in items.socialProfiles {
socialProfiles.append(social.value)
}
for message in items.instantMessageAddresses {
instantMessageAddresses.append(message.value)
}
}
let newContact = CNMutableContact()
newContact.namePrefix = Array(Set(namePrefix))[0]
newContact.givenName = Array(Set(givenName))[0]
newContact.middleName = Array(Set(middleName))[0]
newContact.familyName = Array(Set(familyName))[0]
newContact.previousFamilyName = Array(Set(previousFamilyName))[0]
newContact.nameSuffix = Array(Set(nameSuffix))[0]
newContact.nickname = Array(Set(nickname))[0]
newContact.organizationName = Array(Set(namePrefix))[0]
newContact.departmentName = Array(Set(namePrefix))[0]
newContact.jobTitle = Array(Set(namePrefix))[0]
for item in Array(Set(phoneNumbers)) {
newContact.phoneNumbers.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(emailAddresses)) {
newContact.emailAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(postalAddresses)) {
newContact.postalAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(urlAddresses)) {
newContact.urlAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(contactRelations)) {
newContact.contactRelations.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(socialProfiles)) {
newContact.socialProfiles.append(CNLabeledValue(label: CNLabelHome, value: item))
}
for item in Array(Set(instantMessageAddresses)) {
newContact.instantMessageAddresses.append(CNLabeledValue(label: CNLabelHome, value: item))
}
return newContact
}
}
This approach will take quite a memory so I suggest, use this approach as a reference.
I modified this a little bit. Maybe it helps..
extension Array where Element == String {
var bestElement: String? {
var options: [String : Int] = [:]
for element in self {
if let result = options[element] {
options[element] = result + 1
} else {
options[element] = 1
}
}
return options.sorted { $0.1 > $1.1 }.first?.key
}
}
static func merge(duplicates: [CNContact]) -> CNContact {
// EMPTY CNCONTACT PROPERTIES
var givenName: [String] = []
var familyName: [String] = []
var organizationName: [String] = []
var notes: [String] = []
var phoneNumbers: [CNLabeledValue<CNPhoneNumber>] = []
var emailAddresses: [CNLabeledValue<NSString>] = []
var postalAddresses: [CNLabeledValue<CNPostalAddress>] = []
var urlAddresses: [CNLabeledValue<NSString>] = []
// COLLECT VALUES
for contact in duplicates {
givenName.append(contact.givenName)
familyName.append(contact.familyName)
organizationName.append(contact.organizationName)
notes.append(contact.note)
contact.phoneNumbers.forEach { phoneNumbers.append($0) }
contact.emailAddresses.forEach { emailAddresses.append($0) }
contact.postalAddresses.forEach { postalAddresses.append($0) }
contact.urlAddresses.forEach { urlAddresses.append($0) }
}
// MERGE TO NEW CONTACT
let newContact = CNMutableContact()
newContact.givenName = givenName.bestElement ?? ""
newContact.familyName = familyName.bestElement ?? ""
newContact.organizationName = organizationName.bestElement ?? ""
newContact.note = notes.joined(separator: "\n")
newContact.phoneNumbers = phoneNumbers
newContact.emailAddresses = emailAddresses
newContact.postalAddresses = postalAddresses
newContact.urlAddresses = urlAddresses
return newContact
}
Two things I'm asking myself while reading your function.
newContact.phoneNumbers: It seems like you append ALL the numbers from the 2 contacts. If the 2 duplicated contacts have the same number, then the newContact will then have the same number twice in it's list, right?
It feels like the newContact you are creating is losing lots of information. Like nickname or prefix (Doctor, etc…).
Thanks for this compact code anyway :)

Realm is saving only my most recently added object

I'm trying to save my array of objects in Realm, but Realm appears to be saving only the last object.
This is my model class:
class ContactEntry: Entry {
dynamic var contactId: Int = 0
dynamic var email: String? = ""
dynamic var phone: String? = ""
dynamic var building: String? = ""
dynamic var room: String? = ""
dynamic var contactDescription: String? = ""
dynamic var worktime: String? = ""
dynamic var address: String? = ""
dynamic var personEntries: PersonEntry?
}
This is the base class:
class Entry: Object {
dynamic var id: Int = 0
override static func primaryKey() -> String? { return "id" }
}
This is code I'm using for saving:
func createOrUpdate(entities: [Entity]) {
let entries = toEntries(entities)
let realm = try! Realm()
try! realm.write {
realm.add(entries, update: true)
}
}
func toEntry(entity: Contact) -> ContactEntry {
let entry = ContactEntry()
entry.contactId = entity.id
entry.email = entity.email
entry.phone = entity.phone
entry.building = entity.building
entry.room = entity.room
entry.contactDescription = entity.description
entry.worktime = entity.worktime
entry.address = entity.address
entry.personEntries = personDAO.toEntry(entity.person)
return entry
}
func toEntry(entity: Person) -> PersonEntry {
let entry = PersonEntry()
entry.personId = entity.id
entry.firstname = entity.firstname
entry.middlename = entity.middlename
entry.lastname = entity.lastname
entry.photo = entity.photo
return entry
}
I think that it may be because I have relationship in my model, but I'm not sure why they'd be a problem.
I'm using them as described in the Realm documentation.

Resources