Counting number of instances inside a struct in swift - 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

Related

error: instance member 'tomato' cannot be used on type 'hamburger'

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.

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))
}
}

How to find matches of certain properties in a set of struct instances?

I'm experimenting with structs in Swift 3 and trying to extract/print some values. Here's my code:
struct person {
var name: String
var gender: String
var age: Int
}
var people = [
person(
name: "Steve",
gender: "Male",
age: 25
),
person(
name: "Paul",
gender: "Male",
age: 30
),
person(
name: "Sandra",
gender: "Female",
age: 21
),
person(
name: "Becky",
gender: "Female",
age: 30
),
person(
name: "Trevor",
gender: "Male",
age: 45
)
]
I've learned that I can find the age of a person if I supply the name:
let nameToSearch = "Trevor"
if let i = people.index(where: {$0.name == nameToSearch}) {
print("\(nameToSearch) is aged \(people[i].age)") // Trevor is aged 45
}
What is the best way to...
1) Capture the four oldest people in the group, sort them into descending order by age, and print them like this?
Trevor is aged 45
Paul is aged 30
Becky is aged 30
Steve is aged 25
2) Capture all instances where the gender is "Male", and print them like this?
The males are Steve, Paul, Trevor
And out of interest, assuming that the user was performing actions that led to getting/setting existing instances, as well as adding/removing instances, is there a rough number of person instances in the people array that would cause app performance to take a hit?
A succinct way to get the 4 oldest people:
let oldestPeople = people.sorted(by: { $0.0.age > $0.1.age })[0 ..< 4]
for person in oldestPeople {
print("\(person.name) is aged \(person.age)")
}
And to get the male names:
let maleNames = people.filter({ $0.gender == "Male" }).map({ $0.name })
print("The males are " + maleNames.joined(separator: ", "))
Thank you for steering me in the right direction Hamish. Here are my solutions. To get the four oldest people:
var personAndAge = [String: Int]()
people.forEach {
personAndAge[$0.name] = $0.age
}
var increment = 0
let limit = 4
for (person, age) in personAndAge.sorted(by: { $0.1 > $1.1 }) {
increment = increment + 1
if (increment <= limit) {
print("\(person) is aged \(age)")
}
}
/*
prints:
Trevor is aged 45
Becky is aged 30
Paul is aged 30
Steve is aged 25
*/
To get the males only:
let peopleFilteredToMales = people.filter { $0.gender == "Male"}
var maleNames: [String] = []
peopleFilteredToMales.forEach { maleNames.append($0.name) }
let outputString = "The males are " + maleNames.joined(separator: ", ")
print(outputString)
// prints: The males are Steve, Paul, Trevor

SWIFT append data to dictionary

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
}

Swift: How do I create a predicate with an Int value?

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.