I am creating a sort of tilling system that will take into account offers on certain products. I need to create a half price offer that will display either true or false depending whether the offer has been applied.
HalfPrice Class:
class HalfPriceOffer :Offer {
init(){
super.init(name: "Half Price on Wine")
applicableProducts = [901,902];
}
override func isApplicableToList(list: [ShoppingItem]) -> Bool {
//should return true if a dicount can be applied based on the supplied list of products (i.e. a product must be matched to an offer)
return false
}
ShoppingItem Class
import Foundation
class ShoppingItem {
var name :String
var priceInPence :Int
var productId :Int
init(name:String, price:Int, productId:Int){
self.name = name
self.priceInPence = price
self.productId = productId
}
}
I know it uses loops; but I am unsure of how to actually write it. Thanks in advance :)
You could use the reduce function to achieve this:
func isApplicableToList(list: [ShoppingItem]) -> Bool {
return list.reduce(false) { (initial: Bool, current: ShoppingItem) -> Bool in
return initial || applicableProducts.contains(current.productId)
}
}
You can even write this shorter (Swift is awesome):
func isApplicableToList(list: [ShoppingItem]) -> Bool {
return list.reduce(false) { $0 || applicableProducts.contains($1.productId) }
}
It's perhaps less complex to think whether the offer items are in the list rather than the list items are in the offer.
override func isApplicableToList(list: [ShoppingItem]) -> Bool {
//should return true if a discount can be applied based on the supplied list of products (i.e. a product must be matched to an offer)
let a = list.map({$0.productId})
for p in applicableProducts
{
if !a.contains(p) {return false}
}
return true
}
And here's a full working code example, which fills in the implied gaps in the sample code:
class ShoppingItem {
var name: String
var priceInPence: Int
var productId: Int
init(name:String, price:Int, productId:Int){
self.name = name
self.priceInPence = price
self.productId = productId
}
}
class Offer {
var applicableProducts = [Int]()
var name:String
init(name: String) {
self.name = name
}
func isApplicableToList(list: [ShoppingItem]) -> Bool {
return false
}
}
class HalfPriceOffer: Offer {
init(){
super.init(name: "Half Price on Wine")
applicableProducts = [901,902]
}
override func isApplicableToList(list: [ShoppingItem]) -> Bool {
//should return true if a discount can be applied based on the supplied list of products (i.e. a product must be matched to an offer)
let a = list.map({$0.productId})
for p in applicableProducts
{
if !a.contains(p) {return false}
}
return true
}
}
let a = [ShoppingItem(name: "one", price: 1000, productId: 901), ShoppingItem(name: "two", price: 1009, productId: 907),ShoppingItem(name: "three", price: 1084, productId: 902)]
HalfPriceOffer().isApplicableToList(a) // true
let b = [ShoppingItem(name: "one", price: 1000, productId: 901), ShoppingItem(name: "two", price: 1009, productId: 907)]
HalfPriceOffer().isApplicableToList(b) // false
Related
I have an item of the class Product. I'm changing a variable within the Product class but my addToCart method below treats the item as if no changes have been made. I'm comparing the products based on the id and the variationId. What am I doing wrong?
import UIKit
class Product: Equatable {
let id: Int
let name: String
var variationId: Int
var quantity: Int
init(id: Int, name: String, variationId: Int, quantity: Int) {
self.id = id
self.name = name
self.variationId = variationId
self.quantity = quantity
}
static func == (lhs: Product, rhs: Product) -> Bool {
return
lhs.id == rhs.id && lhs.variationId == rhs.variationId
}
}
The user can select a different color for the product and in doing so changes the variationId.
The addItemToCart() method checks if the cartItems array contains this product. If the product exists, the quantity gets increased by 1 otherwise the product is added to the array.
var cartItems = [Product]()
func addItemToCart(product: Product) {
if cartItems.contains(product) {
let quantity = product.quantity
product.quantity = quantity + 1
} else {
cartItems.append(product)
}
}
The method above keeps updating the quantity regardless if the variationId is different or not.
You are not updating the correct object. Your addItemToCart(product:) function should be something like this:
func addItemToCart(product: Product) {
if let cartItemIndex = cartItems.firstIndex(of: product) {
cartItems[cartItemIndex].quantity += product.quantity
} else {
cartItems.append(product)
}
}
You can do this as follows, you can also remove the equatable attribute.
var cartItems = [Product]()
func addItemToCart(product: Product) {
if let cardItemIndex = cardItems.firstIndex(where: { $0.id == product.id && $0.variationId == product.variationId}) {
cartItems[cardItemIndex].quantity += 1
} else {
cardItems.append(product)
}
}
How do you initialize your classes/structs with a lot of properties?
This question could probably be asked without Swift context but Swift brings a flavour to it, so I add Swift tag in headline and tags.
Let's say you have a User class with 20 properties. Most of them should not be nil or empty. Let's assume these properties are not interdependent. Let's assume that 33% of it should be constant (let) by the logic of the class. Let's assume that at least 65% of them do not have meaningful default values. How would you design this class and initialize an instance of it?
So far I have few thoughts but none of it seems to be completely satisfactory to me:
put all of the properties linearly in the class and make huge init method:
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date
var lastPlayDate : Date
// then HUUUUGE init
init(id: String,
username: String,
...
lastPlayDate: Date) {
}
}
try to group properties into sub types and deal with smaller inits separately
class User {
struct ID {
let id : String
let username : String
let email : String
}
struct Activity {
var lastLoginDate : Date
var lastPlayDate : Date
}
let id : ID
...
var lastActivity : Activity
// then not so huge init
init(id: ID,
...
lastActivity: Activity) {
}
}
another solution is to break requirements a bit: either declare some of the properties optional and set values after init or declare dummy default values and set normal values after init, which conceptually seems to be the same
class User {
// there is 20 properties like that
let id : String
let username : String
let email : String
...
var lastLoginDate : Date?
var lastPlayDate : Date?
// then not so huge init
init(id: String,
username: String,
email: String) {
}
}
// In other code
var user = User(id: "1", username: "user", email: "user#example.com"
user.lastLoginDate = Date()
Is there a nice paradigm/pattern how to deal with such situations?
You can try the builder pattern.
Example
class DeathStarBuilder {
var x: Double?
var y: Double?
var z: Double?
typealias BuilderClosure = (DeathStarBuilder) -> ()
init(buildClosure: BuilderClosure) {
buildClosure(self)
}
}
struct DeathStar : CustomStringConvertible {
let x: Double
let y: Double
let z: Double
init?(builder: DeathStarBuilder) {
if let x = builder.x, let y = builder.y, let z = builder.z {
self.x = x
self.y = y
self.z = z
} else {
return nil
}
}
var description:String {
return "Death Star at (x:\(x) y:\(y) z:\(z))"
}
}
let empire = DeathStarBuilder { builder in
builder.x = 0.1
builder.y = 0.2
builder.z = 0.3
}
let deathStar = DeathStar(builder:empire)
Example taken from here: https://github.com/ochococo/Design-Patterns-In-Swift
If you are looking for a bit more Java like solution, you can try something like this.
Alternative Example
final class NutritionFacts {
private let servingSize: Int
private let servings: Int
private let calories: Int
private let fat: Int
private let sodium: Int
private let carbs: Int
init(builder: Builder) {
servingSize = builder.servingSize
servings = builder.servings
calories = builder.calories
fat = builder.fat
sodium = builder.sodium
carbs = builder.carbs
}
class Builder {
let servingSize: Int
let servings: Int
private(set) var calories = 0
private(set) var fat = 0
private(set) var carbs = 0
private(set) var sodium = 0
init(servingSize: Int, servings: Int) {
self.servingSize = servingSize
self.servings = servings
}
func calories(value: Int) -> Builder {
calories = value
return self
}
func fat(value: Int) -> Builder {
fat = value
return self
}
func carbs(value: Int) -> Builder {
carbs = value
return self
}
func sodium(value: Int) -> Builder {
sodium = value
return self
}
func build() -> NutritionFacts {
return NutritionFacts(builder: self)
}
}
}
let facts = NutritionFacts.Builder(servingSize: 10, servings: 1)
.calories(value: 20)
.carbs(value: 2)
.fat(value: 5)
.build()
Example taken from: http://ctarda.com/2017/09/elegant-swift-default-parameters-vs-the-builder-pattern
So I have a class with the method isApplicableToList(list: [ShoppingItem]) -> Bool. This should return true if a discount can be applied based on the supplied list of product ids (i.e. a product must be matched to an offer) and the product IDs are 901 and 902.
I have attempted it but uncertain if done correctly or if there is a better way.
thanks in advance!
class HalfPriceOffer :Offer {
init(){
super.init(name: "Half Price on Wine")
applicableProductIds = [901,902];
}
override func isApplicableToList(list: [ShoppingItem]) -> Bool {
//should return true if a dicount can be applied based on the supplied list of product ids (i.e. a product must be matched to an offer)
if true == 901 {
return true
}
if true == 902 {
return true
}
else {
return false
}
}
}
ShoppingItem
class ShoppingItem {
var name :String
var priceInPence :Int
var productId :Int
init(name:String, price:Int, productId:Int){
self.name = name
self.priceInPence = price
self.productId = productId
}
}
Loop through the items in your list and test if the item's productId is in the list of applicableProductIds using the contains method. If none is found, return false.
override func isApplicableToList(list: [ShoppingItem]) -> Bool {
//should return true if a dicount can be applied based on the supplied list of product ids (i.e. a product must be matched to an offer)
for item in list {
if applicableProductIds.contains(item.productId) {
return true
}
}
// didn't find one
return false
}
** REWRITE **
OK, it turns out I'm really asking a different question. I understand about hashValue and ==, so that's not relevant.
I would like my wrapper class BUUID to "do the right thing" and act just like NSUUID's act in a Dictionary.
See below, where they don't.
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
Results. Note that I can look up using NSUUID nB and get back what I put under nA. Not so with my BUUID.
a: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
b: 9DE6FE91-D4B5-4A6B-B912-5AAF34DB41C8
a === b: false
a == b: true
Optional("Hi") nil
nA: <__NSConcreteUUID 0x7fa193c39500> BB9F9851-93CF-4263-B98A-5015810E4286
nB: <__NSConcreteUUID 0x7fa193c37dd0> BB9F9851-93CF-4263-B98A-5015810E4286
nA === nB: false
nA == nB: true
Optional("Hi") Optional("Hi")
Inheriting from NSObject also assumes isEqual(object: AnyObject?) -> Bool method overloading:
import Foundation
class BUUID: NSObject {
init?(str: String) {
if let uuid = NSUUID(UUIDString: str) {
_realUUID = uuid
}
else {
return nil
}
}
override init() {
_realUUID = NSUUID()
}
private var _realUUID: NSUUID
override func isEqual(object: AnyObject?) -> Bool {
guard let buuid = object as? BUUID else {
return false
}
return buuid._realUUID == _realUUID
}
override var description: String { get { return _realUUID.UUIDString } }
override var hashValue: Int { get { return _realUUID.hashValue } }
var UUIDString: String { get { print("WARNING Use description or .str instead"); return _realUUID.UUIDString } }
var str: String { get { return _realUUID.UUIDString } }
}
func ==(lhs: BUUID, rhs: BUUID) -> Bool { return lhs._realUUID == rhs._realUUID }
let a = BUUID()
let b = BUUID(str: a.str)!
print("a: \(a)\nb: \(b)")
print("a === b: \(a === b)")
print("a == b: \(a == b)")
var d = [a: "Hi"]
print("\(d[a]) \(d[b])")
let nA = NSUUID()
let nB = NSUUID(UUIDString: nA.UUIDString)!
print("na: \(nA)\nnB: \(nB)")
print("nA === nB: \(nA === nB)")
print("nA == nB: \(nA == nB)")
var nD = [nA: "Hi"]
print("\(nD[nA]) \(nD[nB])")
So the answer is to not make BUUID inherit from NSObject, which undercuts the Swiftiness of overriding ==.
So:
extension BUUID: Hashable {}
class BUUID: CustomStringConvertible {
// take away all 'override' keywords, nothing to override
// otherwise same as above
}
Interesting!
This answer is relevant to initially asked question: Why that's possible to get two key-value pairs with identical key's hashes in a dictionary
This example illustrates that keys in Dictionary can have identical hashes, but equality operation should return false for different keys:
func ==(lhs: FooKey, rhs: FooKey) -> Bool {
return unsafeAddressOf(lhs) == unsafeAddressOf(rhs)
}
class FooKey: Hashable, Equatable {
var hashValue: Int {
get {
return 123
}
}
}
var d = Dictionary<FooKey, String>()
let key1 = FooKey()
let key2 = FooKey()
d[key1] = "value1"
d[key2] = "value2"
Output
[FooKey: "value1", FooKey: "value2"]
That's definitely not good to have all keys with the same hash. In this case we are getting that worst case when search element complexity fells down to O(n) (exhaustive search). But it will work.
What is the right (cleanest and most concise) way to have PetOwner that can at any later point in program create new instances of Cat?
Let's assume that createAnotherAnimal can be called by PetOwner itself after it gets response to some async request, therefore creating as many instances of Cat as needed at the time of creating PetOwner is not possible.
I solved the problem with injecting factory, but I am not convinced that it is the best way to tackle the problem, what are the alternatives in Swinject?
protocol AnimalType {
var name: String? { get set }
func sound() -> String
}
class Cat: AnimalType {
var name: String?
init(name: String?) {
self.name = name
}
func sound() -> String {
return "Meow!"
}
}
protocol PersonType {
func play() -> String
func createAnotherAnimal() -> Void
}
class PetOwner: PersonType {
var pets: [AnimalType] = []
let petFactory : AnimalFactory
init(petFactory : AnimalFactory) {
self.petFactory = petFactory
}
func createAnotherAnimal() {
let pet = petFactory.factoryMethod()
self.pets.append(pet)
}
func play() -> String {
if(pets.count>0) {
let pet : AnimalType = pets[0];
let name = pet.name ?? "someone"
return "I'm playing with \(name). \(pet.sound())"
} else {
return "No animals"
}
}
}
class AnimalFactory {
let factoryMethod : () -> AnimalType
init(factoryMethod: () -> AnimalType) {
self.factoryMethod = factoryMethod
}
}
// Create a container and register service and component pairs.
let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in PetOwner(petFactory: r.resolve(AnimalFactory.self)!) }
container.register(AnimalFactory.self){r in AnimalFactory(factoryMethod:{ () -> AnimalType in r.resolve(AnimalType.self)!}) }
// The person is resolved to a PetOwner with a Cat.
let person = container.resolve(PersonType.self)!
person.createAnotherAnimal()
print(person.play())