I've class and inside of a class got struct GoalsDifference, in that struct there are two propertys: scoredGoal and passedGoal which's value is passed by me.
I've declared two variables with this class and struct and put them in array.
Now I want to get into the array and compare the first variable's scoredGoal to the other one ( as well as passedGoals to each other )
class Team: Base {
var goalsDifference: GoalsDifference?
override init(name:String){
super.init(name: name)
}
convenience init(name:String, goalsDifference:GoalsDifference){
self.init(name:name)
self.goalsDifference = goalsDifference
}
struct GoalsDifference {
var scoredGoal:Int
var passedGoal:Int
}
}
var team1 = Team(name:"Team one", goalsDifference: .init(scoredGoal: 3, passedGoal: 5))
var team2 = Team(name:"Team two", goalsDifference: .init(scoredGoal: 3, passedGoal: 2))
var twoTeams = [team1,team2]
I tried to do it with .filter method but it returns an error : Contextual closure type '(Team) throws -> Bool' expects 1 argument, but 2 were used in closure body
twoTeams[0..<teamsCount].filter { $0.goalsDifference?.scoredGoal ?? "" > $1.goalsDifference?.scoredGoal ?? ""}
This gets a little cumbersome since goalDifference is optional but here is a solution with a function that compares self with another team. I assume that this is sports related and that goals always start at 0.
extension Team {
private static let emptyGoalsDifferenc = GoalsDifference(scoredGoal: .zero, passedGoal: .zero)
func best(goals: KeyPath<GoalsDifference, Int>, comparing other: Team) -> Team? {
let ownDifference = self.goalsDifference ?? Self.emptyGoalsDifferenc
let otherDifference = other.goalsDifference ?? Self.emptyGoalsDifferenc
let ownGoals = ownDifference[keyPath: goals]
let otherGoals = otherDifference[keyPath: goals]
if ownGoals == otherGoals { return nil }
return ownGoals > otherGoals ? self : other
}
}
Example
var team1 = Team(name:"Team one", goalsDifference: .init(scoredGoal: 3, passedGoal: 5))
var team2 = Team(name:"Team two", goalsDifference: .init(scoredGoal: 3, passedGoal: 2))
let bestScored = team1.best(goals: \.scoredGoal, comparing: team2)
let bestPassed = team1.best(goals: \.passedGoal, comparing: team2)
Related
Say I have a struct Coin
struct Coin {
var value: Float?
var country: String?
var color: String?
}
I have two instances of a Coin; we'll call them coinA and coinB.
let coinA = Coin()
coinA.value = nil
coinA.country = "USA"
coinA.color = "silver"
let coinB = Coin()
coinB.value = 50.0
Now, I want to merge the values of coinB into coinA. So the result would be coinA whose values would result in:
country = "USA"
color = "silver"
value = 50.0
I am able to accomplish this with Dictionary objects using the merge() function. However, I am unsure how to accomplish this using custom Swift objects. Is there a way?
Update
Here's how I've gotten it to work with dictionaries:
var originalDict = ["A": 1, "B": 2]
var newDict = ["B": 69, "C": 3]
originalDict.merge(newDict) { (_, new) in new }
//originalDict = ["A": 1, "B": 69, "C": 3]
And I will further clarify, in this function if the newDict does not have keys that the originalDict, the originalDict maintains them.
Ultimately, the most efficient way in the fewest lines of code is probably exactly what you'd expect:
extension Coin {
func merge(with: Coin) -> Coin {
var new = Coin()
new.value = value ?? with.value
new.country = country ?? with.country
new.color = color ?? with.color
return new
}
}
let coinC = coinA.merge(with: coinB)
Note that in the above scenario, the resulting value will always be coinA's, and will only be coinB's if coinA's value for a given key is nil. Whenever you change, add, or delete a property on Coin, you'll have to update this method, too. However, if you care more about future-proofing against property changes and don't care as much about writing more code and juggling data around into different types, you could have some fun with Codable:
struct Coin: Codable {
var value: Float?
var country: String?
var color: String?
func merge(with: Coin, uniquingKeysWith conflictResolver: (Any, Any) throws -> Any) throws -> Coin {
let encoder = JSONEncoder()
let selfData = try encoder.encode(self)
let withData = try encoder.encode(with)
var selfDict = try JSONSerialization.jsonObject(with: selfData) as! [String: Any]
let withDict = try JSONSerialization.jsonObject(with: withData) as! [String: Any]
try selfDict.merge(withDict, uniquingKeysWith: conflictResolver)
let final = try JSONSerialization.data(withJSONObject: selfDict)
return try JSONDecoder().decode(Coin.self, from: final)
}
}
With that solution, you can call merge on your struct like you would any dictionary, though note that it returns a new instance of Coin instead of mutating the current one:
let coinC = try coinA.merge(with: coinB) { (_, b) in b }
I thought it would be interesting to show a solution based on Swift key paths. This allows us to loop somewhat agnostically through the properties — that is, we do not have to hard-code their names in a series of successive statements:
struct Coin {
var value: Float?
var country: String?
var color: String?
}
let c1 = Coin(value:20, country:nil, color:"red")
let c2 = Coin(value:nil, country:"Uganda", color:nil)
var c3 = Coin(value:nil, country:nil, color:nil)
// ok, here we go
let arr = [\Coin.value, \Coin.country, \Coin.color]
for k in arr {
if let kk = k as? WritableKeyPath<Coin, Optional<Float>> {
c3[keyPath:kk] = c1[keyPath:kk] ?? c2[keyPath:kk]
} else if let kk = k as? WritableKeyPath<Coin, Optional<String>> {
c3[keyPath:kk] = c1[keyPath:kk] ?? c2[keyPath:kk]
}
}
print(c3) // Coin(value: Optional(20.0), country: Optional("Uganda"), color: Optional("red"))
There are unfortunate features of key paths that require us to cast down from the array element explicitly to any possible real key path type, but it still has a certain elegance.
If you're willing to make the merge function specific to Coin, you can just use the coalesce operator like so:
struct Coin {
var value: Float?
var country: String?
var color: String?
func merge(_ other: Coin) -> Coin {
return Coin(value: other.value ?? self.value, country: other.country ?? self.country, color: other.color ?? self.color)
}
}
let coinC = coinA.merge(coinB)
This will return a new Coin using the values from coinB, and filling in any nils with those from coinA.
If your goal is to change coin A what you need is a mutating method. Note that structures are not like classes. If you would like to change its properties you need to declare your coin as variable. Note that none of your examples would compile if you declare your coins as constants:
struct Coin {
var value: Float?
var country: String?
var color: String?
mutating func merge(_ coin: Coin) {
value = value ?? coin.value
country = country ?? coin.country
color = color ?? coin.color
}
init(value: Float? = nil, country: String? = nil, color: String? = nil) {
self.value = value
self.country = country
self.color = color
}
}
Playground testing:
var coinA = Coin(country: "USA", color: "silver")
coinA.merge(Coin(value: 50))
print(coinA.country ?? "nil") // "USA"
print(coinA.color ?? "nil") // "silver"
print(coinA.value ?? "nil") // 50.0
This is not a high-level approach like the merge one you shared the link to but as long as you have a struct to implement the merge feature into, it will do the job.
func merge(other: Coin, keepTracksOfCurrentOnConflict: Bool) -> Coin {
var decidedValue = value
if decidedValue == nil && other.value != nil {
decidedValue = other.value
} else if other.value != nil {
//in this case, it's conflict.
if keepTracksOfCurrentOnConflict {
decidedValue = value
} else {
decidedValue = other.value
}
}
var resultCoin = Coin(value: decidedValue, country: nil, color: nil)
return resultCoin
}
}
You can do the same for other properties.
If you want to wrap it around protocol. The idea behind is the same:
you convert object's to dict
merge two dict's
convert merged dict back to your object
import Foundation
protocol Merge: Codable {
}
extension Dictionary where Key == String, Value == Any {
func mergeAndReplaceWith(object: [Key: Value]) -> [Key: Value] {
var origin = self
origin.merge(object) { (_, new) in
new
}
return origin
}
}
extension Merge {
func toJson() -> [String: Any] {
let jsonData = try! JSONEncoder().encode(self)
let json = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any]
return json
}
func merge(object: Merge) -> Merge {
let origin = self.toJson()
let objJson = object.toJson()
let decoder = JSONDecoder()
let merge = origin.mergeAndReplaceWith(object: objJson)
var jsonData = try! JSONSerialization.data(withJSONObject: merge, options: .prettyPrinted)
var mergedObject = try! decoder.decode(Self.self, from: jsonData)
return mergedObject
}
}
struct List: Merge {
let a: String
}
struct Detail: Merge {
struct C: Codable {
let c: String
}
let a: String
let c: C?
}
let list = List(a: "a_list")
let detail_without_c = Detail(a: "a_detail_without_c", c: nil)
let detail = Detail(a: "a_detail", c: Detail.C(c: "val_c_0"))
print(detail.merge(object: list))
print(detail_without_c.merge(object: detail))
Detail(a: "a_list", c: Optional(__lldb_expr_5.Detail.C(c: "val_c_0")))
Detail(a: "a_detail", c: Optional(__lldb_expr_5.Detail.C(c: "val_c_0")))
With this solution you can actually merge two representations of your endpoint, in my case it is List and Detail.
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
I'm making easy example.
I'm trying to filter movies by year using func: filterByYear.
I have an error in one line on the bottom:
'var filterYear = MovieArchive.filterByYear(1980)' Compiler info is: 'Type 'MovieArchive' has no member filterByYear’
import UIKit
class Movie {
let title: String
let director: String
let releaseYear: Int
init(title: String, director: String, releaseYear: Int){
self.title = title
self.director = director
self.releaseYear = releaseYear
}
}
class MovieArchive{
var movies : [Movie]
init(movies:[Movie]){
self.movies = movies
func filterByYear(year:Int) -> [Movie]{
var filteredArray = [Movie]()
for movie in movies{
if movie.releaseYear == year {
filteredArray.append(movie)
}
}
return filteredArray
}
}
}
var newMovie1 = Movie(title: "IT", director: "S.S", releaseYear: 1980)
var newMovie2 = Movie(title: "PP", director: "O.N", releaseYear: 2003)
var moviesArray = [newMovie1, newMovie2]
var myArchive = MovieArchive(movies: moviesArray)
var filterYear = MovieArchive.filterByYear(1980)
var firstMovie = filterYear[0]
print(firstMovie)
You are calling the method on the class itself, not an instance of it. You would have to create an instance of the class like this:
let myMovieArchive = MovieArchive(movies: [Some Array])
and then call the method on the instance.
print(myMovieArchive.filterByYear)
So in your case, call it on myArchive, not MovieArchive.
Even though the question has been answered, you can simplify your filterByYear method to make it a bit more Swifty:
func filterByYear(year:Int) -> [Movie]
{
return movies.filter({ $0.releaseYear == year })
}
I have two classes "Receipe" and "Incredient". A receipe can have a list of incredients. Now, I want that when I pass in a list of incredient I should get all the receipies back which contain that incredient. Here is what I have:
How can I filter the receipies based on the passed in incrediants.
class Recipe {
var name :String!
var incredients :[Incredient]!
init(name :String, incredients :[Incredient]) {
self.name = name
self.incredients = incredients
}
}
class Incredient {
var name :String!
init(name :String) {
self.name = name
}
}
var incredientsToSearchFor = [Incredient(name:"Salt"),Incredient(name :"Sugar")]
var receipe1 = Recipe(name: "Receipe 1", incredients: [Incredient(name: "Salt"),Incredient(name :"Pepper"),Incredient(name :"Water"),Incredient(name :"Sugar")])
var receipe2 = Recipe(name: "Receipe 2", incredients: [Incredient(name: "Salt"),Incredient(name :"Pepper"),Incredient(name :"Water"),Incredient(name :"Sugar")])
var receipe3 = Recipe(name: "Receipe 3", incredients: [Incredient(name :"Pepper"),Incredient(name :"Water"),Incredient(name :"Sugar")])
var receipies = [receipe1,receipe2,receipe3] // list of all the recipies
func getRecipiesByIncrediants(incredients :[Incredient]) -> [Recipe] {
// WHAT TO DO HERE
return nil
}
let matchedRecipies = getRecipiesByIncrediants(incredientsToSearchFor)
A few things need to change.
First, these are data models, so they should probably be struct types instead of class. This allows you to remove the initializer and optionals too:
struct Recipe : Equatable {
let name: String
let ingredients: [Ingredient]
}
struct Ingredient : Equatable {
let name : String
}
You'll notice I also made them Equatable. This is so you can use contains to find them in an array. Without it, contains won't know whether two of these types are equal.
To conform to Equatable, just add the == methods:
func ==(lhs: Ingredient, rhs: Ingredient) -> Bool {
return lhs.name == rhs.name
}
func ==(lhs: Recipe, rhs: Recipe) -> Bool {
return lhs.name == rhs.name && lhs.ingredients == rhs.ingredients
}
Creating your data is roughly the same. I fixed some spelling errors and changed var to let since these values don't change:
let ingredientsToSearchFor = [Ingredient(name:"Salt"), Ingredient(name :"Sugar")]
let recipe1 = Recipe(name: "Recipe 1", ingredients: [Ingredient(name: "Salt"), Ingredient(name :"Pepper"), Ingredient(name :"Water"), Ingredient(name :"Sugar")])
let recipe2 = Recipe(name: "Recipe 2", ingredients: [Ingredient(name: "Salt"), Ingredient(name :"Pepper"), Ingredient(name :"Water"), Ingredient(name :"Sugar")])
let recipe3 = Recipe(name: "Recipe 3", ingredients: [Ingredient(name :"Pepper"), Ingredient(name :"Water"), Ingredient(name :"Sugar")])
let recipes = [recipe1, recipe2, recipe3] // list of all the recipes
Now on to the filtering. There's more efficient ways to do this, but for ease of reading you can use filter and reduce:
func getRecipesByIngredients(incredients :[Ingredient]) -> [Recipe] {
return recipes.filter { recipe in
incredients.reduce(true) { currentValue, ingredient in
return currentValue && (recipe.ingredients.contains(ingredient))
}
}
}
filter returns a new array containing only elements where the block returns true. reduce consolidates an entire array into one value (in this case true or false). So we iterate through each recipe, and check whether all of the specified ingredients are in it.
To call the method:
let matchedRecipes = getRecipesByIngredients(ingredientsToSearchFor)
This returns recipes 1 and 2 because both contain salt and sugar.
If you want recipes that contain salt OR sugar, use reduce(false) and change && to ||.
I'm at it again with swift arrays and containsObject provided by NSArray only!
I bridge the swift array to NSArray to do that contains:
extension Array {
func contains(object:AnyObject!) -> Bool {
if(self.isEmpty) {
return false
}
let array: NSArray = self.bridgeToObjectiveC();
return array.containsObject(object)
}
}
it works fine in general but as soon as I put a String! in an array of type String, it crashes. Even though containsObject does take a AnyObject!
var str : String! = "bla"
var c = Array<String>();
c.append(str)
println(c.contains(str))
declaring a String! array also doesn't help
var str : String! = "bla"
var c = Array<String!>();
c.append(str)
println(c.contains(str))
BUT the same without ! works fine
var str : String = "bla"
var c = Array<String>();
c.append(str)
println(c.contains(str))
SO how do I explicitly wrap stuff? I don't really see why I'd have to explicitly wrap it only so it is right unwrapped but that's what it looks like.
Swift 1:
let array = ["1", "2", "3"]
let contained = contains(array, "2")
println(contained ? "yes" : "no")
Swift 2, 3, 4:
let array = ["1", "2", "3"]
let contained = array.contains("2")
print(contained ? "yes" : "no")
Swift
If you are not using object then you can user this code for contains.
let elements = [ 10, 20, 30, 40, 50]
if elements.contains(50) {
print("true")
}
If you are using NSObject Class in swift. This variables is according to my requirement. you can modify for your requirement.
var cliectScreenList = [ATModelLeadInfo]()
var cliectScreenSelectedObject: ATModelLeadInfo!
This is for a same data type.
{ $0.user_id == cliectScreenSelectedObject.user_id }
If you want to AnyObject type.
{ "\($0.user_id)" == "\(cliectScreenSelectedObject.user_id)" }
Full condition
if cliectScreenSelected.contains( { $0.user_id == cliectScreenSelectedObject.user_id } ) == false {
cliectScreenSelected.append(cliectScreenSelectedObject)
print("Object Added")
} else {
print("Object already exists")
}
Generally, when you want to have an array that contains a custom object or struct, and you want to work with "contains" function, your class or struct should be conformed to "Equatable" protocol and you should implement the "==" function for later comparisons...
struct booy: Equatable{
static func == (lhs: booy, rhs: booy) -> Bool {
return lhs.name == rhs.name
}
var name = "abud"
}
let booy1 = booy(name: "ali")
let booy2 = booy(name: "ghasem")
var array1 = [booy]()
array1.append(booy1)
array1.append(booy2)
let booy3 = booy(name: "ali")
if array1.contains(booy3){
print("yes") }