Custom NSCoding object, Swift (map) - ios

I have this class :
class Point: NSCoding {
var x: String?
var y: Double?
override func mapping(map: Map) {
self.x <- map["x"]
self.y <- map["y"]
}
...
In my ViewModel I have a function that generates an array of Double.
I need to create a var with those Double to the y parameters of Points, and 2 string to the x parameters.
I've done something like this but this was with a Point object so everything fits.
let points = PointObject.map { point in
Point(x: point.x ?? "0", y: point.y ?? 0.0).self
}
Any suggestion/help? I'm not very familiar with this

Given two arrays of String/x and Double/y values:
let xValues = ["0", "1"]
let yValues = [2.0, 3.0]
you can create Points using zip and map
let points = zip(xValues, yValues).map {
Point(x: $0.0, y: $0.1)
}

Related

Find longest length string in array of tuples

I have an array of tuples var label = [(x: CGFloat, y: CGFloat, v1: String, v2: String)]() and would like to find the string of longest length in v1.
I could iterate through the array like this:
var maxlength = 0
var position = 0
for i in 0 ..< label.count {
if label[i].v1.count > maxlength {
maxlength = label[i].v1.count
position = i
}
}
print("longest string: \(maxlength) chars, at position \(position), is \(label[position].v1)")
but is there a more concise method?
You can use the max(by:) operator on your label array.
let longestString = label.max { $0.v1 < $1.v1}
Or a combination of map and max, as you're only interested in the longest string, not the tuple with the longest string value.
let longestString = label.map(\.v1).max()
For finding the position you have 2 options
enumerated:
let positionOfLongestString: Int? = label
.map(\.v1)
.enumerated() // converts array to a (offset: Index, element: String) tuple array
.max { $0.element < $1.element }?
.offset
firstIndexOf:
let positionOfLongestString: Int? = label
.map(\.v1)
.max()
.flatMap { longestString in
label.firstIndex { $0.v1 == longestString }
}

Array map, Swift

I have a custom Rules class.
class Rules: NSCoding {
var x: String?
var y: Double?
override func mapping(map: Map) {
self.x <- map["x"]
self.y <- map["y"]
}
In my viewModel I need to create an object rules and to pass 1 by 1 the elements of two array. The first array is composed by 3 strings, the second array has some Double (more than 3 !!)
This is what I tried so far:
let xValues = ["String1", "String2", "String3"]
let yValues = [1.0, 2.0, 1.5, 2.5, 5.1, 6.0, 8.0]
let rules = zip(xValues, yValues).map {
Rules(x: "\($0.0)", y: $0.1)
}
The problem with this (I guess) is that my rules object has some duplicated string or sometimes more than what I have in my xValues array. (It's possible I am doing something wrong somewhere else ...)
What I need is to pass exactly three strings, and a number of Double which is different, lets say 6 double.
Since zip only returns tuples for those indices where both input arrays have have values, you'll need a method that fills up the smaller array instead, like this:
func zipFill<T, U>(_ arr1: [T], _ arr2: [U]) -> [(T?, U?)] {
let c1 = arr1.count
let c2 = arr2.count
let count = max(c1, c2)
var result = [(T?, U?)]()
for i in 0..<count {
if i < c1 && i < c2 {
result.append((arr1[i], arr2[i]))
} else if i >= c1 {
result.append((nil, arr2[i]))
} else if i >= c2 {
result.append((arr1[i], nil))
}
}
return result
}
let xValues = ["String1", "String2", "String3"]
let yValues = [1.0, 2.0, 1.5, 2.5, 5.1, 6.0, 8.0]
let rules = zipFill(xValues, yValues).map {
Rules(x: $0.0, y: $0.1)
}
print(rules)
// [ {x "String1", y 1}, {x "String2", y 2}, {x "String3", y 1.5},
// {x nil, y 2.5}, {x nil, y 5.1}, {x nil, y 6}, {x nil, y 8} ]
Why don't you just remove duplicates before creating your rules?
Define a generic extension to remove duplicates:
extension RangeReplaceableCollection {
func removingDuplicates<E>(keyPath path: KeyPath<Element, E>) -> Self
where E: Hashable
{
var seen = Set<E>()
seen.reserveCapacity(count)
var new = self
new.removeAll { element -> Bool in
if seen.contains(element[keyPath: path]) {
return true
} else {
seen.insert(element[keyPath: path])
return false
}
}
return new
}
}
Then remove duplicates before zipping:
let xValues = ["String1", "String2", "String3"].removingDuplicates(keyPath: \.self)
let yValues = [1.0, 2.0, 1.5, 2.5, 5.1, 6.0, 8.0].removingDuplicates(keyPath: \.self)
let rules = zip(xValues, yValues).map {
Rules(x: $0.0, y: $0.1)
}
Tidbit: you don't need to use String interpolation for the x argument, because the parameter $0.0 is already a String.

Find the 4 closest positions of X and Y on a list

I have the following class :
class IBeacon {
var x = Int()
var y = Int()
var ID = Int()
init(id: Int, x :Int, y: Int) {
self.x = x
self.y = y
self.ID = id
}
}
and in another class I have a beacons list:
var listIBeacon = [IBeacon]()
Problem: I get my coordinate X (Int) and Y (Int) and I would know the 4 closest IBeacon in the list listIBeacon to me.
Add a helper function to compute distances between beacons (we can drop the pesky square root since we are only comparing distances):
extension IBeacon {
func distanceSquared(to beacon: IBeacon) -> Int {
let dx = beacon.x - self.x
let dy = beacon.y - self.y
return dx*dx + dy*dy
}
}
then sort using the distanceSquared function we just implemented:
var listIBeacon = [IBeacon]()
let myBeacon: IBeacon = ...
listIBeacon.sort {
let dist0 = myBeacon.distanceSquared(to: $0)
let dist1 = myBeacon.distanceSquared(to: $1)
return dist0 < dist1
}
finally, filter the closest 4 beacons:
let closestBeacons = Array(listIBeacon.prefix(4))

Unable to assign class objects to dictionary member of another class object in Swift 3

I have a class ChessSquare with its own attributes and methods. I have another class ChessBoard, which has an attribute of dictionary type.
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
.....
One of the methods of the ChessBoard class builds the chessSquare object and assigns it to chessBoard object as below.
self.squares[squareName] = chessSquare
Before Swift3 this code was working fine. After upgrading to Swift3 the assignment stopped working.
Using breakpoints I see that chessSquare objects are built as expected. squareName variable has the expected value. The "self", which is the chessBoard object has proper initial values. But the dictionary assignment gets skipped without any error in the above mentioned code.
Is this anything related to Swift3. I browsed and could not get specific solution to my case.
Adding more code for clarity.
class ChessSquare {
let squareColor: UIColor
let squareShade: Int
let squareName: Int
let squareSize: CGSize
let minXY: CGPoint
let maxXY: CGPoint
let squareOrigin: CGPoint
var hasPiece = false
weak var chessPiece: ChessPiece?
let squareSprite: SKSpriteNode
let squareType: SquareType
//more data members
init(squareColor: UIColor, squareShade: Int, squareName: Int, squareSize: CGSize, minXY: CGPoint, maxXY: CGPoint, squareOrigin: CGPoint, squareSprite: SKSpriteNode, squareType: SquareType) {
self.squareColor = squareColor
self.squareShade = squareShade
self.squareName = squareName
self.squareSize = squareSize
self.minXY = minXY
self.maxXY = maxXY
self.squareOrigin = squareOrigin
self.squareSprite = squareSprite
self.squareType = squareType
}
//more methods
}
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
var byteBoard: [UInt8] = [UInt8](repeating: UInt8(0), count: 66)
var byteBoardHashVal: Int = 0
// [ byteBoardHashVal : ((byteBoard, evalnVal), repCnt) ]
var byteBoards: [Int : (([UInt8],Double?), Int)] = [:]
var TT: [Int : Double] = [:]
var TTReuseCount = 0
var whitePawnlessFileByte: UInt8 = 255
var blackPawnlessFileByte: UInt8 = 255
var maxXY: CGPoint = CGPoint()
var minXY: CGPoint = CGPoint()
var oldTouchedSquare: ChessSquare?
init() {
squares = [:]
byteBoard = [UInt8](repeating: UInt8(0), count: 66)
byteBoardHashVal = 0
byteBoards = [:]
TT = [:]
TTReuseCount = 0
whitePawnlessFileByte = 255
blackPawnlessFileByte = 255
}
func drawFlippedChessBoard(_ view: SKView, scene: GameScene) {
....
....
for row in 0..<8 {
for col in 0..<8 {
let squareName = row * 8 + col
....
let chessSquare = ChessSquare(squareColor: currentColor, squareShade: squareShade, squareName: squareName, squareSize: squareSize, minXY: minXY, maxXY: maxXY, squareOrigin: squareOrigin, squareSprite: squareSprite, squareType: squareType)
....
self.squares[squareName] = chessSquare
....
}
....
}
....
}
//more methods
}
I have a difficulty in understanding if squareName is a string or a integer. It sounds like a string, I would suggest to name it chessSquareIndex or squareIndex if in case it is Int and you want to index chess square.
Based on your comment above I did a small test to verify that it works correctly if squareName is Int and here is the result,
class ChessSquare { }
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
func getChessSquare(at squareIndex: Int) {
let chessSquare = ChessSquare()
return squares[squareIndex] = chessSquare
}
}
And this seems to work fine with Swift 3. Also notice that it is not required to call self, unless you are inside escaping closures.

How to choose from an array of sprites in swift

So basically I am looking to choose one of the 4 different coloured balls at random to come into the scene which each have an animation, physics properties and movement & spacing that I have already coded. I am not sure exactly how to make the array then choose at random from the array of the 4 coloured balls so that I have one ball chosen at random to come into the scene.
To make it more clear what I'm asking here's some code (I only use two balls in this code so you don't have to read as much):
var moveandremove = SKAction() < this is in my ballScene.swift
The spawn runBlock is inside didMovetoView
let spawn = SKAction.runBlock({
() in
self.allballs()
})
let delay = SKAction.waitForDuration(2.0)
let SpawnDelay = SKAction.sequence([spawn, delay])
let spawndelayforever = SKAction.repeatActionForever(SpawnDelay)
self.runAction(spawndelayforever)
let distance = CGFloat(brnball.frame.width * 20 + brnball.frame.width)
let moveball = SKAction.moveByX(-distance, y: 0, duration: NSTimeInterval(0.003 * distance))
let removeball = SKAction.removeFromParent()
moveandremove = SKAction.sequence([moveball])
}
func allballs() {
TextureAtlasblk = SKTextureAtlas(named: "blkball")
for i in 1...TextureAtlasblk.textureNames.count{
var Name = "blkball_\(i)"
blkarray.append(SKTexture(imageNamed: Name))
}
blkball = SKSpriteNode(imageNamed: "blkball_1")
blkball.position = CGPoint(x: CGRectGetMidX(self.frame) + 100, y: CGRectGetMidY(self.frame))
blkball.zPosition = 7
blkball.setScale(0.1)
self.addChild(blkball)
blkball.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(blkarray, timePerFrame: 0.2)))
//brownball
TextureAtlasbrn = SKTextureAtlas(named: "brnball")
for i in 1...TextureAtlasbrn.textureNames.count{
var Name = "brnball_\(i)"
brnarray.append(SKTexture(imageNamed: Name))
}
brnball = SKSpriteNode(imageNamed: "brnball_1")
brnball.position = CGPoint(x: CGRectGetMidX(self.frame) + 50, y: CGRectGetMidY(self.frame))
brnball.zPosition = 7
brnball.setScale(0.1)
self.addChild(brnball)
brnball.runAction(SKAction.repeatActionForever(SKAction.animateWithTextures(brnarray, timePerFrame: 0.2)))
Here is my terrible starting point at trying to make an array to choose from each ball (this is inside my allballs() function):
var ballarray: NSMutableArray = [blkball, brnball, yelball, bluball]
runAction(moveandremove)
I am new to swift and pretty hopeless, would be awesome if someone could help me out :)
Thanks
It's hard for me to find the array that you're talking about in your code. But nevertheless, I can still show you how.
Let's say we have an [Int]:
let ints = [10, 50, 95, 48, 77]
And we want to get a randomly chosen element of that array.
As you may already know, you use the subscript operator with the index of the element to access an element in the array, e.g. ints[2] returns 95. So if you give a random index to the subscript, a random item in the array will be returned!
Let's see how can we generate a random number.
The arc4random_uniform function returns a uniformly distributed random number between 0 and one less the parameter. Note that this function takes a UInt32 as a parameter and the return value is of the same type. So you need to do some casting:
let randomNumber = Int(arc4random_uniform(UInt32(ints.count)))
With randomNumber, we can access a random element in the array:
let randomItem = ints[randomNumber]
Try to apply this technique to your situation.
Here's a generic method to do this as well:
func randomItemInArray<T> (array: [T]) -> T? {
if array.isEmpty {
return nil
}
let randomNumber = Int(arc4random_uniform(UInt32(array.count)))
return array[randomNumber]
}
Note that if the array passed in is empty, it returns nil.
You could make and extension for Array that returns a random element.
extension Array {
func randomElement() -> Element {
let i = Int(arc4random_uniform(UInt32(count - 1)))
return self[i]
}
}
You could take that a step further and allow a function to be applied directly to a random element.
mutating func randomElement(perform: (Element) -> Element) {
let i = Int(arc4random_uniform(UInt32(count - 1)))
self[i] = perform(self[i])
}
You can use this function when using an array of reference types.
func randomElement(perform: (Element) -> ()) {
let i = Int(arc4random_uniform(UInt32(count - 1)))
perform(self[i])
}

Resources