[Swift[ How to iterate over all the properties of an object - swift

I have an object defined with about 15 properties. I am trying to iterate over all of the properties that aren't equal to zero and are of type Int or Double. Something like this:
/*
object.price1 = 10.0
object.price2 = 9.9
object.price3 = 8.9
object.price4 = 10.1
object.name = "banana"
object.type = "fruit"
object.quantitySold = 0
object.dateIntroduced = ""
*/
let banana = object()
for property in banana {
object.property = property*2
}
Any ideas on how to accomplish this?

This isn't an easy thing to do in Swift (although possible), but that's no bad thing: being able to iterate over an object's properties and mutate (change) them without having to directly reference a property by name could easily lead to some confusion for you or another developer later on, when you're trying to find out why a property of an object has changed.
Much better instead to make this kind of operation explicit, and name it correctly. Something like the following:
extension Object {
func doubledPrice() -> Object {
return Object(
price1: price1 * 2,
price2: price2 * 2,
price3: price3 * 2,
price4: price4 * 2,
name: name, //we can't double a string
type: type,
quantitySold: quantitySold, //I've named the func assuming you won't double the quantitySold, obviously if that's not the desired behaviour then this needs to change
dateIntroduced: dateIntroduced //can't double a date
)
}
}

Make the prices an array? This is from my phone so check for errors. The bad side to this is how messy it can be and how difficult it would be to keep organized.
class MyProduct{
var price1 : Int
var price2 : Int
var price3 : Int
var pricesArray : [Int]
init(price1 : Int, price2 : Int, price3 : Int, pricesArray : [Int]){
self.price1 = price1
self.price2 = price2
self.price3 = price3
for i in 0...2
{ pricesArray.append(0)}
pircesArray[0] = price1
pricesArray[1] = price2
pricesArray[2] = price3
self.pricesArray = pricesArray
}_
//then to go over it like
for i in 0...3{
banana.pricesArray[i] = banana.procesArray[i] * 2
}
Or you could make a function in the product class
func equate( sum : Int)
{
yourVar = yourVar * sum
}

Related

Swift optionals in class methods

I am still relatively new to swift, so I am having some problems with the proper syntax. Here is my code for the class Date, which has the isLeapYear and daysInMonth methods. I am having problems with the optionals for these methods:
class Date {
var day, month, year : Int
init (day : Int, month : Int, year : Int) {
self.day = day
self.month = month
self.year = year
}
func isLeapYear(y : Int? = self.year) -> Bool {
var x = false
if y % 4 == 0 {x = true}
return x
}
//Returns the amount of days in a given month
func daysInMonth(month : Int? = self.month, year : Int? = self.year) -> Int? {
let _31_day_months = [1, 3, 5, 7, 8, 10, 12]
let _30_day_months = [4, 6, 9, 11]
if month == 2 {
if self.isLeapYear(y : year) {return 29} else {return 28}
}
else if _31_day_months.contains(month) {return 31}
else if _30_day_months.contains(month) {return 30}
else {return nil}
}
}
What I want to do with func isLeapYear(y : Int? = self.year) -> Bool is that when I call isLeapYear and y isn't specified, that it is automatically set to self.year. However I get the following error:
use of unresolved identifier 'self'
I also get the error
value of optional type 'Int?' must be unwrapped to a value of type
'Int'
I know I have to use !, but I don't know exactly how and where, I have tried doing if y! % 4 == 0, but that just seemed to make it worse.
I would also like to do the same thing for the method daysInMonth
Default values need to be constant at compile-time. You can't define them in terms of some other property. You need to check their values at runtime. In your example this would be:
func isLeapYear(y : Int? = nil) -> Bool {
var x = false
if (y ?? year) % 4 == 0 {x = true} // <== "?? year" meaning "y, or year if y is nil"
return x
}
Note that this is a very confusing API. You'd have to create a random Date instance in order to check something unrelated to that instance. Instead what I believe you really mean here is two methods; one static and one on the instance:
// Static method, called as Year.isLeapYear(xxxx)
static func isLeapYear(_ y: Int) -> Bool {
// Note that this is not the correct Gregorian Leap Year rule
return y % 4 == 0
}
// Instance method, called as year.isLeapYear()
func isLeapYear() -> Bool { Date.isLeapYear(year) }
Since you're new to Swift, it's worth noting: This should be a struct, not a class (it is a pure value that has no identity, and any two Dates with the same properties should be considered the same Date, which is what structs are for). And you should be careful calling it "Date" since that collides with the Foundation type of the same name.

Parsing units and amounts from text in swift

I was wondering what the best approach would be to separate multiple items from a string in swift. I'm hoping to separate a unit and an amount from a string and then use those values to create an object of my ingredient class.
For example:
var string = "4 cups sugar"
I would need to grab the 4 (amount) and convert it to an int and then grab the unit (cups)
var amount = 4
var unit = "cups"
Another example:
"3 large eggs"
In this case I would want to pull out the 3 (amount) and the unit would be empty.
var amount = 3
var unit = ""
Then I would create my object using the unit and amount values.
I'm still a novice at swift, and more-so with string manipulation so I'm not entirely sure how to approach this, any help in the right direction would be great.
I am working with an ingredient class that is structured as:
class IngredientModel {
var amount = 0
var unit = ""
init(amount : Int, unit : String) {
self.amount = amount
self.unit = unit
}
In SWIFT you can do this early with optionals & generics. I can show you how? with your example..
Your Ingredient class
class IngredientModel {
var amount = 0
var unit = ""
let units = ["cups","spoon"] //Add your required units
init(string: String) {
let array = string.components(separatedBy: " ")
if let _amount = array.ref(0) {
self.amount = Int(_amount) ?? 0
}
if let _unit = array.ref(1),
units.contains(_unit){
self.unit = _unit
}
}
}
An Array Extension to prevent index out of range
extension Array {
func ref(_ i:Int) -> Element? {
return 0 <= i && i < count ? self[i] : nil
}
}
If I'm reading the apple documentation right, you should use the components(separatedBy) function. The parameter should be one space in quotes like so:
let array = yourString.components(separatedBy: " ")

Optional value in swift 3 Optimised way

I have create class Testing model which has 4 dataMember it should not be null when accessing (means return default value)
extension Double {
/// Rounds the double to decimal places value
func roundTo(places:Int = 2) -> Double
{
let divisor = pow(10.00, Double(places))
return (self * divisor).rounded() / divisor
}
}
class TestingModel{
var id : String!
var name : String! = "abc" /*It is not working*/
var price : Double! = 0.00
var uniqueId : Int! = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? "" //I dont want to do like this way
name = dictionary["name"] as? String
price = (dictionary["price"] as? Double)?.roundTo() ?? 0.00
uniqueId = dictionary["unique_id"] as? Int
}
}
let t:TestingModel = TestingModel.init(dictionary: ["x id" : "x012y12345z45","x name":"test1","x price":100.0,"uniqueId":1236.0])
let testString = "Jd " + t.id
print(testString) //Perfect
print(t.name)
print(t.price) /* Only one decemal point is printed */
Getting Output
Jd
nil
0.0
Expected output
Jd
abc /Should return abc instead of nil/
0.00 /Two decimal point complulsury/
What i actually mean in
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
price is a Double type and what you are asking to do is to print that double value to 2 decimal places. Then you should make use of the following.
let a = 0.0
print(String(format: "%.2f", a))
this prints:
0.00
If you are planning to round it to decimal places, then also the above code will return that. But if you need it to round and return a double type then you can check this answer
Based on your updated question, I suggest to use the model as follows:
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.0
var uniqueId : Int = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? ""
name = dictionary["name"] as? String ?? "abc"
price = (dictionary["price"] as? Double) ?? 0.0
uniqueId = dictionary["unique_id"] as? Int ?? 1
}
}
You seem to have asked two different questions here. The first one regarding doubles have already been answered by adev. I will answer the second one, which is:
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
If you want to do this then it means that the variable shouldn't be optional at all, as nil isn't one of its valid values. Make the variable a non-optional type and give it a default value.
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.00
var uniqueId : Int = 1
}
You can't really avoid using ?? in the constructor because of the nature of dictionaries. They will always return a nil value if the key does not exist. You have to check it. It does not make sense even if this is possible anyway. Imagine something like this:
someVariable = nil // someVariable is now 0
This is extremely confusing. someVariable is 0, even though it appears that nil is assigned to it.
A workaround will be to add a dictionary extension. Something like this:
extension Dictionary {
func value(forKey key: Key, defaultValue: Value) -> Value {
return self[key] ?? defaultValue
}
}
But I still recommend that you use ?? instead.

How get value from variables of similar names [duplicate]

This question already has an answer here:
Swift, get variable name from a string
(1 answer)
Closed 6 years ago.
Sequential variable names defined. I want to reach a variable relative to the corresponding value.
var code = 2 // 5, 6, ... // corresponding values
var name : String
var name1 : String = "Aaaaaa"
var name2 : String = "Bbbbbb"
// var name3
// var name4 ...
name = "name" + String(code) // name = "Bbbbb" ??
I do not get the results I wanted.
I hope i explained correctly. Thank you for your help.
Unlike in Objective-C, you can't access a variable by a String of its name in Swift. In your case, you might want to just use an array:
var code = 2 // 5, 6, ... // corresponding values
var names = ["Aaaaaa", "Bbbbbb"]
var name = names[code - 1] // "Bbbbbb"
If you really want to do it dynamically from a string name, you'll have to bridge to Objective-C, for example:
class SomeObject: NSObject {
var name1 : String = "Aaaaaa"
var name2 : String = "Bbbbbb"
func getName(code: Int) -> String {
let name = valueForKey("name" + String(code)) as! String // "Bbbbbb"
return name
}
}
SomeObject().getName(2) // "Bbbbbb"
Of course, by doing this, you lose a lot of the safety that Swift provides.

Create Simple Class with multiple calculating function( )

Trying to run a simple calculation via a function in my class. I simply want to add bill1 + bill2 and print the total amount spent on bills. So (bill1 + bill2 = total). And then print the total amount.
Current error states - "Code after 'return' will never be executed." Now, is my location for my print in the wrong location or did I declare my variables incorrectly? Should I be using vars instead of lets?
What do you recommend for my function in order to calculate and print the result?
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
//totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
*Error println("You spend \(totalBillsPerMonth)")
}
}
First: "Code after 'return' will never be executed."
Yes it will not, after you call return you exit the function and return to the function that call it, you probably have an warning in XCode warning you about telling you that
Second: "Should I be using vars instead of lets"
If the value changes you MUST use var, if it does not you SHOULD use let.
Some problems I can see in your code:
class BillsCalculator
{
//use _ in the beginning of the name for class variables
//eg. _nameOfBill instead nameOfBill1
//It is not wrong use nameOfBill1 is just not recommended
//if nameOfBill1 change use var
let nameOfBill1: String = "Medical"
//Why is this declare twice
let nameOfBill1: String = "Hulu"
//Those values look like change should be var
var monthlyBillAmount1: Double = 34.25
var monthlyBillAmount2: Double = 7.99
var calculateTotalsPerMonth: Double = 0.0
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
totalBillsPerMonth = add(monthlyBillAmount1 + monthlyBillAmount2)
//print before return
println("You spend \(totalBillsPerMonth)")
return totalBillsPerMonth(monthlyBillAmount1 + monthlyBillAmount2)
}
}
Here you should either print the value of your total bill or return that value. As you just want to print the total bill amount so I would recommend you to just print, not to return anything. You can refer the below code.
class BillsCalculator
{
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
let monthlyBillAmount1: Double = 34.25
let monthlyBillAmount2: Double = 7.99
let calculateTotalsPerMonth: Double = 0.0
//calculateTotalPerMonth ( = monthlyBillAmount_1 + monthlyBillAmount_2 + 3)
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double
{
calculateTotalsPerMonth= add(monthlyBillAmount1 + monthlyBillAmount2)
println("You spend : "+totalBillsPerMonth);
}
}
One tiny error in your code
let nameOfBill1: String = "Medical"
let nameOfBill1: String = "Hulu"
These two variables have the same name, perhaps one should be:
let nameOfBill2: String = "Hulu"
And yes return is always the last line in the function, so any codes after return will never be executed. If you only want to get the total of two bills, you can simply do this:
func calculateTotalsPerMonth(monthlyBillAmount: Double, monthlyBillAmount2: Double) -> Double {
//println("You spend \(totalBillsPerMonth)")
return monthlyBillAmount1 + monthlyBillAmount2
}
and call this function with your bill variables, like:
let bill1 = 34.25
let bill2 = 7.99
let totalBill = calculateTotalsPerMonth(bill1, bill2)
println("You spent \(totalBill)")
Swift is a very smart language, and it is type safe. You can remove the type if you want, more like a personal programming style thing.
let bill1: Double = 34.25
let bill1 = 34.25
They both will be type "Double"
As others have said, you need to put your println statement before return since returns ends the execution of the method; thus println will never be run.
However, I would suggest a few changes to your current approach:
// A bill is an object - why not encapsulate it in a struct.
struct Bill {
let name: String
let amount: Double
}
// Using structs is generally preferred, unless you need inheritance and/or
// references to your BillsCalculator objects.
struct BillsCalculator {
let bill1: Bill
let bill2: Bill
// Using a read-only computed property means you don't need to set
// the total to have an initial value of zero.
var totalBilled: Double {
return bill1.amount + bill2.amount
}
}
// Since you're probably going to want to reuse BillsCalculator,
// don't have each bill set already. Instead, use BillsCalculator's
// initialiser and pass in bills.
let bill1 = Bill(name: "Medical", amount: 34.25)
let bill2 = Bill(name: "Hulu", amount: 7.99)
let cal = BillsCalculator(bill1: bill1, bill2: bill2)
print("You've spend \(cal.totalBilled) this month")