Verify and swap elements swift - swift

Sorry for the beginner question and my bad english.
I need to make a function in my class Team that will check my array to see if the player exists, if doesn't, it will return an error, if it exists, it will make an swap between the two array.
Example: I have an array with starters and reserve players, players 1,2 and 3 are starters, and players 4,5 and 6 are substitutes.
so when I want to do a player swap, I can use something like: Team.swapfunc(player1, player4) or something similar
My example code:
class SoccerPlayers {
let name: String
let number: Int
init(name: String, number: Int) {
self.name = name
self.number = number
}
}
class Team {
var nameTeam: String
// Array of players
var startingPlayers:[SoccerPlayers] = []
var reservePlayers:[SoccerPlayers] = []
init(nameTeam:String, startingPlayers: [SoccerPlayers], reservePlayers: [SoccerPlayers]) {
self.nameTeam = nameTeam
self.startingPlayers = startingPlayers
self.reservePlayers = reservePlayers
}
}
var player1: SoccerPlayers = SoccerPlayers(
name: "Andre",
number: 1
)
var player2: SoccerPlayers = SoccerPlayers(
name: "Joao",
number: 2
)
var player3: SoccerPlayers = SoccerPlayers(
name: "Matheus",
number: 3
)
var player4: SoccerPlayers = SoccerPlayers(
name: "Junior",
number: 4
)
var player5: SoccerPlayers = SoccerPlayers(
name: "Fabio",
number: 5
)
var player6: SoccerPlayers = SoccerPlayers(
name: "Paulo",
number: 6
)
let team1 = Team(nameTeam:"YourTeam", startingPlayers:[player1, player2, player3], reservePlayers:[player4, player5, player6])
}
obs: this code is only for study, not a real working project

Let's break this down, what we need to do is to
Check if players exists in the corresponding array
Remove them from the array
Insert them into the other array
To check if a player exist we want to be able to compare two players, we can either do that manually or make the player type correspond to Equatable and also decide what makes two player equal, here I decided that it is the number that uniquely defines a player so we are using that.
struct SoccerPlayer: Equatable { // Conform to Equatable
let name: String
let number: Int
// This function will automatically be used whenever some other function want's to check if two players are equal
static func == (lhs: SoccerPlayer, rhs: SoccerPlayer) {
lhs.number == rhs.number
}
}
Note that I have changed from class to struct because that is what is recommended for this kind of data/functionality. I also change the name to SoccerPlayer since each instance holds a single player
In the swap function we need to check for a player and then remove it from the array, looking at the Array type we see it has a remove function that takes an index (position in the array) as an argument so we want to get the index of each player first.
The function firstIndex(of:) is perfect here since it returns an index or nil if nothing was found so we can also use it for our validation.
let playerIndex = startingPlayers.firstIndex(of: player)
here playerIndex will be nil if the array doesn't contain the player, also not that pass the player as argument to the function and this can be done since it conforms to Equatable
To perform the check and get the result in one go we use a guard statement. And once we have the index we can remove the players from one array and add them to the other. Here is the full swap function that belongs to Team
mutating func swap(player: SoccerPlayer, substitute: SoccerPlayer) -> Bool {
guard let playerIndex = startingPlayers.firstIndex(of: player),
let substituteIndex = reservePlayers.firstIndex(of: substitute) else {
return false
}
// Use the indices to remove players
startingPlayers.remove(at: playerIndex)
reservePlayers.remove(at: substituteIndex)
// Add them to the other array
startingPlayers.append(substitute)
reservePlayers.append(player)
return true
}
Note that I have also changed Team to a struct and that the mutating keyword is used to tell the compiler we are updating the struct.
I didn't really understand what you meant by returning an error so I simply return a boolean instead.
As an extra check you could test that each player doesn't exists in the array we are going to add it to

Related

Clear way to update some nested struct from a bigger struct in place

Say we have some complex struct with multiple nested levels(for simplicity, in the example will be only one level, but there could be more).
Example. We have a data structure:
struct Company {
var employee: [Int: Employee]
}
struct Employee {
var name: String
var age: Int
}
var company = Company(employee: [
1: Employee(name: "Makr", age: 25),
2: Employee(name: "Lysa", age: 30),
3: Employee(name: "John", age: 28)
])
Now we want to create a function which updates some Employee of the company in place. We could write it using an inout param:
func setAge(_ age: Int, forEmployee employee: inout Employee) {
employee.age = age
}
setAge(26, forEmployee: &company.employees[1]!)
This works, but as you can see we need to unwrap expression 'company.employees[1]' before passing it by ref. This forced unwrap can produce runtime error if there is no such employee for the provided key.
So we need to check if the employee exists:
if company.employees[1] != nil {
setAge(26, forEmployee: &company.employees[1]!)
}
This also works, but this code is kind of ugly because we need to repeat the expression 'company.employees[1]' two times.
So the question is: Is there some way to get rid of this repetition?
I tried to use optional inout param in the modifying function but could not get it working.
Based on your comments, like
What I wanted in the first place is just to have a reference to a substructure of a bigger structure so the part of code that is dealing with the substructure could know nothing about where is this substructure located in the bigger structure.
and
It would be ideal if I just could create a local inout var. Like if var employ: inout Employee? = company.employee[1] { // make whatever I want with that employee }.
I think that what you want is a generic update function. In the community this is part of the family of utility functions referred as with (https://forums.swift.org/t/circling-back-to-with/2766)
The version that you need in this case is one that basically guards on nil, so I suggest something like
func performUpdateIfSome <T> (_ value: inout T?, update: (inout T) throws -> Void) rethrows {
guard var _value = value else { return }
try update(&_value)
value = _value
}
with this utility then what you wanted to do would be done with
performUpdateIfSome(&company.employees[1], update: { $0.age = 26 })
Note
If you want to abstract away how to access the employee but not the company, then keypaths are an option as well :)
You need to hide the implementation and let the struct handle the logic with specific error handling strategy, like throwing an error or simply return true/false depending on success or simply ignore any problems. I don't know what the Int key stands for but here I guess it's an ID of some sort, so add this to Company struct
mutating func setAge(_ age: Int, forId id: Int) -> Bool {
if employee.keys.contains(id) {
employee[id]?.age = age
return true
}
return false
}
I would simply add extension to Employee which set employee's age
extension Employee {
mutating func setAge(_ age: Int) {
self.age = age
}
}
Then you can use optional chaining for calling. So if value for key 1 doesn't exist, nothing happens and code goes on
company.employee[1]?.setAge(26)
Edit:
If your goal is just to change some property and then return object, simply create method which takes optional parameter and returns optional value
func setAge(_ age: Int, forEmployee employee: inout Employee?) -> Employee? {
employee?.age = age
return employee
}
if let employee = setAge(26, forEmployee: &company.employees[1]) { ... }

Swift for loop is creating new objects

I'm working on a game where I store my player objects in an array. I want to give these players a hand of cards and I do so in the following way:
var players = [Player]()
mutating func deal(count: Int) {
for var player in players {
for _ in 1...count {
if let card = deck.draw(){
player.addCard(card: card)
}
}
if (player.id==1){print(player)}
}
print(players[0])
}
struct Player{
var id : Int
private(set) var randomValue: Int = 0
private(set) var hand = [PlayingCard]()
init(id: Int) {
self.id = id
randomValue = 100.arc4random
}
mutating func addCard(card: PlayingCard){
hand.append(card)
}
}
The problem is the hand is not actually being updated. Printing the player at index 0 (player.id == 1) inside the for loop gives me exactly what I expect. However, the print outside the loop has an empty hand and the random number is different. So I know the loop is creating a new object since init is called. Why is this happening and how can I edit the actual object in players array?
player is struct, when you iterate players - you get a copy of the players in the array.
You update it, print it and see the correct result, but it's scope is for the for loop only.
Either make player a class or update your array with the new copy of the player.

What is a KeyPath used for?

In Swift 4 many on the Foundation team have discussed how much easier it is to use keyPaths as compared to Swift 3. This begs the question... What is a keyPath? Seriously, I can't find any clear resources.
Objective-C has the ability to reference a property dynamically rather than directly. These references, called keypaths. They are distinct from direct property accesses because they don't actually read or write the value, they just stash it away for use.
Let define a struct called Cavaliers and a struct called Player, then create one instance of each:
// an example struct
struct Player {
var name: String
var rank: String
}
// another example struct, this time with a method
struct Cavaliers {
var name: String
var maxPoint: Double
var captain: Player
func goTomaxPoint() {
print("\(name) is now travelling at warp \(maxPoint)")
}
}
// create instances of those two structs
let james = Player(name: "Lebron", rank: "Captain")
let irving = Cavaliers(name: "Kyrie", maxPoint: 9.975, captain: james)
// grab a reference to the `goTomaxPoint()` method
let score = irving.goTomaxPoint
// call that reference
score()
The last lines create a reference to the goTomaxPoint() method called score. The problem is, we can't create a reference to the captain's name property but keypath can do.
let nameKeyPath = \Cavaliers.name
let maxPointKeyPath = \Cavaliers.maxPoint
let captainName = \Cavaliers.captain.name
let cavaliersName = irving[keyPath: nameKeyPath]
let cavaliersMaxPoint = irving[keyPath: maxPointKeyPath]
let cavaliersNameCaptain = irving[keyPath: captainName]
Please test with Xcode 9 or capable snapshot.
Swift KVC
[Objective-C KVC]
KeyPath is a reference to a property of a type rather than value. It adds a dynamism into language
There are some of them:
KeyPath<Root, Value> - only read
WritableKeyPath<Root, Value> - read/write for var property
ReferenceWritableKeyPath<Root, Value> - read/write only for reference types[About]
PartialKeyPath<Root>
AnyKeyPath
You can find that KeyPath is used for shortcuts(e.g. sorting, iterating, filtering), KVO[Example], MemoryLayout[Example], SwiftUI and others more advanced features
Syntax
class SomeClass {
var v: String = "Hello World"
}
Pre Swift v4 - slow and not type safe
#objc //For example var v: String = "Hello world"
let keyPath = #keyPath(SomeClass.v) //keyPath is String Type
//read
someClass.value(forKeyPath: keyPath) //or forKey:
//write
someClass.setValue("Another string", forKeyPath: keyPath) //or forKey:
Starts from backwards slash \
let someKeyPath = \SomeClass.v //KeyPath<SomeClass, String>
If there is a known object you can use \.
someClass.observe(\.v, options: .new) //someClass1.v
//read
let res = someClass[keyPath: \SomeClass.v]
//write
someClass[keyPath: \.v] = "Another string"

How to act atomically on a struct property?

Assume I have the following objects:
class Dog {
let name: String
var stats: Stats?
func doSomething() { /*...*/ }
}
struct Stats {
var weight: Double
var age: Int
func ageEquivalence() -> Int { /*...*/ }
}
... and that I want to perform some calculation on a Dog instance. I want to do this atomically, however. In other words, I want to guarantee that either all code is executed, or no code is executed.
To clarify the problem, first, let's assume that Stats were a class. In that case, this problem is easy to solve:
func updateAge(_ dog: Dog) {
guard let stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
It's either going to going to execute lines A-C if stats exists, or do nothing.
Now let's go back to the original problem, assume Stats is a struct again. If I try to run the code above, first I must change let stats to var stats, otherwise the compiler will complain that I'm trying to update a constant.
func updateAge(_ dog: Dog) {
guard var stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
But now we run in to the problem that stats is a copy of dog.stats so when I update the age on line A, I'm not actually modifying the original dog's age.
Our next approach might look like this:
func updateAge(_ dog: Dog) {
dog.stats?.age += 1 // A
dog.doSomething() // B
if let stats = dog.stats {
sendNotification(stats.ageEquivalence()) // C
}
}
The problem with this approach is that if doSomething has a side effect which sets stats = nil, we would have run lines A & B, but not the C.
What's the best way to solve this problem?
It seems that the crux of the problem is this line:
dog.stats?.age += 1
In order to modify a struct, we must act on the original object, meaning we must use optional chaining. This cannot be used in conjunction with a guard var since that would create a copy of the original struct, and the original struct might be changed due to a side effect at any point of the execution.

Swift: How to assign a variable by reference, not by value?

I'm trying to get a reference to an Array and make modifications to it. Because Arrays in Swift are value types, instead of reference types, if I assign my array to a variable first, I am getting a copy of the array instead of the actual array:
var odds = ["1", "3", "5"]
var evens = ["2", "4", "6"]
var source = odds
var destination = evens
var one = odds.first!
source.removeFirst() // only removes the first element of the `source` array, not the `odds` array
destination.append(one)
When we look at the odds and evens arrays, they are unaltered because we changed the source and destination arrays.
I know that I can use the inout parameter attribute on a function to pass them by reference, instead of by value:
func move(inout source: [String], inout destination: [String], value:String) {
source.removeAtIndex(source.indexOf(value)!)
destination.append(value)
}
move(&odds, destination: &evens, value:one)
Is there a way to assign these arrays to a variable by reference, instead of by value?
Array is a struct, which means it's a value type in Swift. Because of this, arrays always behave according to value and not reference semantics. The problem here is that you're attempting to use mutable, reference based logic to operate on values types.
You don't want to rely on mutations occurring inside the function to propagate back to the caller. As you've found, this is only possible with inout parameters. What you should do instead is return the mutated array from the function back to the caller. The point of value oriented programming is that it shouldn't matter which array you have, but rather that any two equivalent arrays or values types are interchangeable.
It's slightly easier to imagine with another value type. Take an Int for example, and this function that does some math.
func addFive(int: Int) -> Int {
return int + 5
}
Now consider a similar function, but written in the reference oriented style that you're attempting to use:
func addFive(inout int: Int) {
int = int + 5
}
You can see it's simply not natural to operate on value types this way. Instead just return the updated value (the modified arrays) from your function and carry on from there.
Here is your function refactored with value semantics.
func move(source: [String], destination: [String], value:String) -> ([String], [String]) {
var mutableSource = source
var mutableDestination = destination
mutableSource.removeAtIndex(source.indexOf(value)!)
mutableDestination.append(value)
return (mutableSource, mutableDestination)
}
let (updatedSource, updatedDestination) = move(odds, destination: evens, value:one)
You cannot assign an array to a variable by reference in Swift.
"In Swift, Array, String, and Dictionary are all value types..."
Source: https://developer.apple.com/swift/blog/?id=10
If you need arrays that can be manipulated by reference you can create a class that encapsulates an array and use it for your variables.
here's an example:
class ArrayRef<Element>:CustomStringConvertible
{
var array:[Element]=[]
init() {}
init(Type:Element.Type) {}
init(fromArray:[Element]) { array = fromArray }
init(_ values:Element ...) { array = values }
var count:Int { return array.count }
// allow use of subscripts to manipulate elements
subscript (index:Int) -> Element
{
get { return array[index] }
set { array[index] = newValue }
}
// allow short syntax to access array content
// example: myArrayRef[].map({ $0 + "*" })
subscript () -> [Element]
{
get { return array }
set { array = newValue }
}
// allow printing the array example: print(myArrayRef)
var description:String { return "\(array)" }
// delegate append method to internal array
func append(newElement: Element)
{ array.append(newElement) }
// add more delegation to array methods as needed ...
}
// being an object, the ArrayRef class is always passed as a reference
func modifyArray(X:ArrayRef<String>)
{
X[2] = "Modified"
}
var a = ArrayRef("A","B","C")
modifyArray(a)
print(a) // --> a is now ["A", "B", "Modified"]
// various means of declaration ...
var b = ArrayRef<String>()
b[] = ["1","2","3"]
var c = ArrayRef(fromArray:b[])
// use .array to modify array content (or implement delegation in the class)
c.array += a[] + ["X","Y","Z"]
Note that you could also define your arrays as NSMutableArrays which are Obj-C classes and are passed by reference. It does a similar thing and does present differences with a regular array for the methods that are available.
I recommend this the following only for didactic purpose only, I advise against using it in production code.
You can circulate a "reference" to something via an UnsafePointer instance.
class Ref<T> {
private var ptr: UnsafePointer<T>!
var value: T { return ptr.pointee }
init(_ value: inout T) {
withUnsafePointer(to: &value) { ptr = $0 }
}
}
var a = ["1"]
var ref = Ref(&a)
print(a, ref.value) // ["1"] ["1"]
a.append("2")
print(a, ref.value) // ["1", "2"] ["1", "2"]
ref.value.removeFirst()
print(a, ref.value) // ["2"] ["2"]
Thus, you can simulate a reference to a variable via the above class, which stores a pointer to the given variable reference.
Please note that this is a simple use case, and will behave as expected only if if the variable doesn't get destroyed before the pointer, as in that case the memory initially occupied by the variable will be replaced by something else, and the unsafe pointer will no longer be valid. Take for example the next code:
var ref: Ref<[String]>!
// adding an inner scope to simulate `a` being destroyed
do {
var a: [String] = ["a"]
ref = Ref(&a)
print(a, ref.value)
a = ["b"]
print(a, ref.value)
}
// `a` was destroyed, however it's place on the stack was taken by `b`
var b: [String:Int] = ["a": 1]
// however `b` is not an array, thus the next line will crash
print(ref.value)