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))
}
}
Related
1.what I code
class hamburger {
var tomato: String
var patty: String
var bread: String
var number: Int
init(_ tomato: String, _ patty: String, _ bread: String, _ number: Int) {
self.tomato = tomato
self.patty = patty
self.bread = bread
self.number = number
}
init() {
self.tomato = "tomato"
self.patty = "patty"
self.bread = "bread"
self.number = 10
}
}
let sandwich = hamburger("texas" , "iii" , "iii" , 10)
print(hamburger.tomato)
2.error message
Playground execution failed:
error: dotinstall.playground:342:7: error: instance member 'tomato'
cannot be used on type 'hamburger'
print(hamburger.tomato)
^~~~~~~~~ ~~~~~~
3.The sample I followed
enter code here// Class
class User {
let name: String // property
var score: Int
init(name: String, score: Int) {
init(_ name: String, _ score: Int) {
self.name = name
self.score = score
}
init() {
self.name = "bob"
self.score = 30
}
}
//let tom = User(name: "tom", score: 23)
let tom = User("tom", 23)
print(tom.name)
print(tom.score)
let bob = User()
print(bob.name)
print(bob.score)
I have coded like 1 following 3, but I got a error message like 2.
what I did to solve
・anyway follow this sample to be like same
・studied a basic of class syntax, initializer, instance on website
・was looking for a mistypes
・I checked the order of property
I don't why it is not worked even if I just follow the sample code.
please give me tips on the solution.
thanks
You're making a mistake regarding object oriented programming. With hamburger.tomato you try to access the property tomato of the Class Hamburger, not the Object, which is sandwich here. So the solution would be:
print(sandwich.tomato)
In the future, what you might want to do is take a look at styling your code better. Classes(Hamburger) are written starting with an uppercased letter, while the objects, or instances(sandwich), of the Classes are written starting with a lowercased letter.
I really just joined this community to get help with one particular question, specifically, on how to declare a type that holds a variety of information types in a specific order. For context, I have the code example that I'm using below.
typealias Template = [String, Array<Any>, String, String]
This in turn will be used both to store data and to filter through it in further functions as follows...
var HamburgerToppings: [String] = []
var Salads: [String] = []
let Example: [Template] = [ABC, HamburgerToppings, XY, ZZ]
let Example2: [Template] = [DEF, Salads, GH, IJ]
var ExampleList: [Template] = [Example, Example2]
func DoTheThing() {
for Template in ExampleList {
if Template[0] == DEF {
print("SALADS!")
} else {
print("BURGERS!)
}
}
}
If anyone could figure out what I'm doing wrong, or if there is a completely different declaration that needs to be made, I would be very grateful for the advice.
Many thanks,
-P. OAK
A fixed-length collection of values is a tuple, not an array. What you meant was this (note parentheses, not brackets).
typealias Template = (String, Array<Any>, String, String)
That said, I think what you meant was this:
typealias Template = (String, [String], String, String)
let hamburgerToppings: [String] = []
let salads: [String] = []
let example: Template = ("ABC", hamburgerToppings, "XY", "ZZ")
let example2: Template = ("DEF", salads, "GH", "IJ")
let exampleList: [Template] = [example, example2]
func doTheThing() {
for template in exampleList {
if template.0 == "DEF" {
print("SALADS!")
} else {
print("BURGERS!")
}
}
}
You should not use a tuple here, though, at all. This should be a struct, like this:
struct Template {
let identifier: String
let toppings: [String]
let something2: String
let something3: String
}
let hamburgerToppings: [String] = []
let salads: [String] = []
let example = Template(identifier: "ABC", toppings: hamburgerToppings, something2: "XY", something3: "ZZ")
let example2 = Template(identifier: "DEF", toppings: salads, something2: "GH", something3: "IJ")
let exampleList: [Template] = [example, example2]
func doTheThing() {
for template in exampleList {
if template.identifier == "DEF" {
print("SALADS!")
} else {
print("BURGERS!")
}
}
}
Tuples tend to be fairly inflexible. They can be useful for quick, one-time anonymous types (particularly as return values), but by the time you're putting a typealias on them, you almost certainly meant to use a struct.
You can use tuple instead:
typealias Template = (String, Array<Any>, String, String)
...
let Example: Template = (ABC, HamburgerToppings, XY, ZZ)
...
func DoTheThing() {
for Template in ExampleList {
if Template.0 == DEF {
print("SALADS!")
} else {
print("BURGERS!)
}
}
}
Tuples
Tuples group multiple values into a single compound value. The values
within a tuple can be of any type and do not have to be of the same
type as each other.
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#Tuples
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
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
}
I am getting an EXC-BAD-ACCESS error on this statement:
var thisPredicate = NSPredicate(format: "(sectionNumber == %#"), thisSection)
thisSection has an Int value of 1 and displays the value 1 when I hover over it. But in the debug area I see this:
thisPredicate = (_ContiguousArrayStorage ...)
Another predicate using a String shows as ObjectiveC.NSObject
Why is this happening?
You will need to change %# for %i and remove the extra parenthesis:
Main problem here is that you are putting an Int where it's expecting an String.
Here's an example based on this post:
class Person: NSObject {
let firstName: String
let lastName: String
let age: Int
init(firstName: String, lastName: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
override var description: String {
return "\(firstName) \(lastName)"
}
}
let alice = Person(firstName: "Alice", lastName: "Smith", age: 24)
let bob = Person(firstName: "Bob", lastName: "Jones", age: 27)
let charlie = Person(firstName: "Charlie", lastName: "Smith", age: 33)
let quentin = Person(firstName: "Quentin", lastName: "Alberts", age: 31)
let people = [alice, bob, charlie, quentin]
let thisSection = 33
let thisPredicate = NSPredicate(format: "age == %i", thisSection)
let _people = (people as NSArray).filteredArrayUsingPredicate(thisPredicate)
_people
Another workaround would be to make thisSection's value an String, this can be achieved by String Interpolation or via description property of the Int.. lets say:
Changing:
let thisPredicate = NSPredicate(format: "age == %i", thisSection)
for
let thisPredicate = NSPredicate(format: "age == %#", thisSection.description)
or
let thisPredicate = NSPredicate(format: "age == %#", "\(thisSection)")
of course, you can always bypass this step and go for something more hardcoded (but also correct) as:
let thisPredicate = NSPredicate(format: "sectionNumber == \(thisSection)")
But take into account that for some weird reason
String Interpolation (this kind of structure: "\(thisSection)") where leading to retain cycles as stated here
When your data is safe or sanitized, you might try String Interpolation Swift Standard Library Reference. That would look something like this:
let thisSection = 1
let thisPredicate = NSPredicate(format: "sectionNumber == \(thisSection)")
On 64 bit architecture an Int maps to an Int64 and %i will overflow if its value is greater than 2,147,483,648.
You will need to change %# for %ld and remove the extra parenthesis.