SWIFT append data to dictionary - swift

I'm having trouble coding an apparently simple task. I want to add new client profile data to a client profile dictionary (clientDatabase) but keep getting errors - can't seem to append - error: value of type '(String: clientProfile)' has no member 'append' (see error at bottom of code)
Any insights you can provide are greatly appreciated.
Thanks,
Bill
//: Playground - noun: a place where people can play
import UIKit
import Foundation
/*
code copied from B-C Dev Database - structs but simplified with fewer variables
goal is to getappend new client to work.
*/
/*
Globals: these go in main.swift file
*/
struct clientProfile {
var firstName: String = ""
var lastName: String = ""
var group: Int = 0
}
//var clientDatabase : NSMutableDictionary! = [String:clientProfile]()
var clientDatabase:[String:clientProfile]
/* sample data template: phone is key, sub array is;
(firstName: "", lastName: "",pilatesGroup: )
*/
clientDatabase = [
"1234567": clientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": clientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": clientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": clientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
clientDatabase.count
// so far so good
/*
add new client
declare local variables in scene swift files where used
*/
var firstName: String = ""
var phone:String = ""
var newPhone: String = ""
var newFirstName: String = ""
var newLastName: String = ""
var newGroup: Int = 0
// define struct using these input variables for values but with same keys as (global) clientDatabase
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
// input values from scene - UITextFields
newPhone = "4567890"
newFirstName = "Super"
newLastName = "Dave"
newGroup = 14
// test that all values are where they should be
clientDatabase
clientDatabase.count
newClientDatabase = [newPhone:newClientProfile()]
newClientDatabase.count
// ok so far
//the following line returns an error
clientDatabase.append(newClientDatabase)
// can't seem to append - error value of type '(String: clientProfile)' has no member 'append'

Two things. First of all clientDatabase is a dictionary which doesn't have append, instead you'll have to iterate through the other dictionary and insert its elements into clientDatabase.
The other issue is that clientDatabase and newClientDatabase aren't the same type. The first one is [String : clientProfile] and the second is [String : newClientProfile]. You'll have to convert the values from one type to the other to combine the dictionaries.
Looking deeper into the code there some misunderstandings about the language. For example:
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
You're creating a struct just for the purpose of containing a single set of values when you already have clientProfile. Instead you could do:
var newClientProfile = clientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
This will create a variable which is an instance of clientProfile and stores the information you want. However, you have the other variables defined as empty values.
Here's a cleaned up version of your code, take a look at it and let me know if you have any questions.
struct ClientProfile { // Convention is to use initial caps for enum, struct, class
let firstName: String
let lastName: String
let group: Int
}
var clientDatabase = [
"1234567": ClientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": ClientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": ClientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": ClientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
// input values from scene - UITextFields
let newPhone = "4567890"
let newFirstName = "Super"
let newLastName = "Dave"
let newGroup = 14
// define struct using these input variables for values but with same keys as (global) clientDatabase
let newClientProfile = ClientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
let newClientDatabase = [newPhone:newClientProfile]
for (phone,client) in newClientDatabase {
clientDatabase[phone] = client
}

Related

How can I properly display a much readable output using sorting?

The output of my code is all good, it is already sorted, but the problem is that, it contains some garbage value that I do not need, I will provide the example output of it.
Here is my code:
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0...studs.count - 1{
print("Student \(i+1) \t \(student[i].id)\t\(student[i].name)\t\t\(student[i].course)\t\t\(student[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: student)
print(x)
Here is the Output of the Given Code
As you can see the output displays some values that is not needed.
What I want to be displayed is a better readable sorted of values given.
Thank You!
If you make your custom classes conform to the CustomStringConvertible protocol (add a single computed variable, description, of type String) then when you print one of those objects it displays nicely formatted.
You could use the formatting of your print statement with tabs as the starting point.
The function stud is already printing your student array in a formatted way.
Remove print(x) at the end of the code you posted to get a clean output.
Edit:
Also if I understand correctly your needs, you want to print the sorted list of students by GPA. (x in your code)
You can do it by passing x to the stud function and by fixing the stud function to use the function parameter instead of student var.
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0..<studs.count {
print("Student \(i+1) \t \(studs[i].id)\t\(studs[i].name)\t\t\(studs[i].course)\t\t\(studs[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: x)

create objects/instances in variables swift

I don't know how to use variables when creating Instances or adressing them in Swift:
For exmaple how do I do following in a loop (creating Instances):
class Guest {
let name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let guests = [["ann", 1] , ["bob", 2] ...]
so that the loop equals :
let ann = Guest(name: "ann" , age: 1)
let bob = Guest(name: "bob" , age: 2)
...
edit: I am looking for something like this:
for i in guests {
let i[0] = Guest(name: i[0] , age: i[1])
Example for adressing:
print(guests[0].age)
>>>1
I've searched a lot but am getting directed to issues regarding creating variables in classes.
Thank you very much!
You can do that with a classic loop:
let input = [("Ann", 1), ("Bob", 2)]
var guests: [Guest] = []
for each in input {
guests.append(Guest(name: each.0, age: each.1))
}
However, it can be done more concisely (and with avoidance of var) using functional techniques:
let guests = [("Ann", 1), ("Bob", 2)].map { Guest(name: $0.0, age: $0.1) }
EDIT: Dictionary-based solution (Swift 4; for Swift 3 version just use the classic loop)
let input = [("Ann", 1), ("Bob", 2)]
let guests = Dictionary(uniqueKeysWithValues: input.map {
($0.0, Guest(name: $0.0, age: $0.1))
})
Or, if it's possible for two guests to have the same name:
let guests = Dictionary(input.map { ($0.0, Guest(name: $0.0, age: $0.1)) }) { first, second in
// put code here to choose which of two conflicting guests to return
return first
}
With the dictionary, you can just do:
if let annsAge = guests["Ann"]?.age {
// do something with the value
}
//MARK: Call method to create multiple instances
createInstance([("Ann", 1), ("Bob", 2)])
func createInstance(_ input: Array<Guest>) {
for each in input {
guests.append(Guest(name: each.0, age: each.1))
}
}

Counting number of instances inside a struct in swift

I'm very new to Swift and I can't figure this one out. I need to count the number of instances created inside a struct. Since I created 3 instances, how can I get the program to tell me there are three? I tried the exNames.count at the end, but that doesn't work... Thanks!
struct People {
let name: String
var age: Int
let sex: Character
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
let exNames = [People]()
exNames.count
You want to use a static variable on the People struct. However, this does require overriding the default initializer.
struct People
{
static var instances = 0
let name:String
var age:Int
let sex:Character
init(name:String, age:Int, sex:Character)
{
self.name = name
self.age = age
self.sex = sex
People.instances += 1
}
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
let exNames = [People]()
/* exNames.count only gives the number of People that are
contained in this particular array, which is zero. */
print(People.instances) // 3
If you want to decrement the count when the structs go out of scope, you need to upgrade to a class which provides a deinitializer deinit {}.
Note that the “proper” use cases for a static counter are exceedingly limited. It is very likely that the problem you are actually trying to solve would be better served by a different hammer.
By the way, you really shouldn’t be using Character to represent sex, as Character in Swift is very closely tied to strings, and so they are built and optimized for lexical purposes, not for flagging. It also opens the door for a lot of potential bugs, as Swift won’t be able to verify valid input as well (what if someone accidentally passes a sex value of "#"?) Instead, use the built in Bool type, or a custom enum if you need more functionality.
Looks like you wanted to create an array of people, in that case:
struct People {
let name: String
var age: Int
let sex: Character
}
var heather = People(name: "Heather", age: 32, sex: "F")
var peter = People(name: "Peter", age: 34, sex: "M")
var scott = People(name: "Scott", age: 27, sex: "M")
//This should be a var, because you are going to modify it
var exNames = [People]()
exNames.append(heather)
exNames.append(peter)
exNames.append(scott)
exNames.count

How to use Swift struct implicit initializer?

Here is the code I have:
struct Algorithm {
let category: String = ""
let title: String = ""
let fileName: String = ""
}
let a = Algorithm(
The autocomplete shows the only valid initializer:
But I was expecting to use the implicit initializer like
Algorithm(category: "", title: "", fileName: "")
This gives the error:
Argument passed to call that takes no arguments
There are even screenshots on another issue that shows this call being successfully used.
What am I doing wrong?
The problem is the let. If you declare your properties with var, you'll get the memberwise initializer:
struct Algorithm {
var category: String = ""
var title: String = ""
var fileName: String = ""
}
let alg = Algorithm(category: "", title: "", fileName: "")
But since you supplied default values for all the properties, you don't get the memberwise initializer for let properties. The rule is that you get the implicit memberwise initializer for stored properties without a default value and for var properties (provided you have no explicit declared initializer).
You provided the default values for the properties that's why compiler won't add the default initialiser. When you remove the default values you will get what you are expecting:
struct Algorithm {
let category: String
let title: String
let fileName: String
}
use
struct Algorithm {
var category: String = ""
var title: String = ""
var fileName: String = ""
}
and autocomplete will show you both possibilities

Extract value from struct that meet condition

I have a struct for different animals, and values for these animals. Im adding animals to it.
struct Animal {
var type: String
var weight: String
var cost: String
}
var animals = [Animal]()
func addAnimal(type: String, weight: String, cost: String){
animals.append(Animal(type: type, weight: weight, cost: cost))
}
addAnimal("monkey", "80", "300")
addAnimal("zebra", "200", "500")
addAnimal("monkey", "50", "250")
I want to say, if type == "monkey" then return all weights for monkeys. In this example I would want the code to return values "80" and "50".
I'm new to coding so any advice on this would be helpful. Thank you
You can combine filter and map to accomplish what you want as follow:
let monkeysWeights = animals.filter{$0.type == "monkey"}.map{$0.weight}
println(monkeysWeights) // ["80", "50"]