Swift trying to cross check array of SKActions with array of strings - arrays

Hi I'm trying to pick an SKAction from an array of SKAction's by using a string.
I have an array with all my game's possible SKActions in it. I then need to pull out the particular actions that match a selected node's possible action names (strings).
So, for example, I might have
allActions = [runCentre, runLeft, runRight]
whereby those are SKActions, and
possibleActions = [runCentre, runRight]
which are strings accessed from a property list relating to the given node type.
How do I query the allActions array for the values in possibleActions? I know the mechanics of iterating through both arrays but not what I would actually try to access. Can I give the SKActions a string property somehow? I tried
runStraight.setValue("runStraight", forKey: "name")
but that throws up NSUnknownKeyException. Is there a better way to do this?
Any help would be very much appreciated!

I really don't understand very well what do you want to achieve.
Unfortunately a SKAction don't have a tag or name property as identification.
You can create a dictionary to associate a string key to a SKAction
Suppose you have these actions:
let runCentre = SKAction.move(to: CGPoint(x:100.0,y:100.0), duration: 1.0)
let runLeft = SKAction.move(to: CGPoint(x:0.0,y:100.0), duration: 1.0)
let runRight = SKAction.move(to: CGPoint(x:200.0,y:100.0), duration: 1.0)
let runTop = SKAction.move(to: CGPoint(x:100.0,y:200.0), duration: 1.0)
let runBottom = SKAction.move(to: CGPoint(x:100.0,y:0.0), duration: 1.0)
// create a dictionary (key:string, value:SKAction)
let allActions = ["runCentre":runCentre,"runLeft":runLeft,"runRight": runRight,"runTop":runTop,"runBottom":runBottom]
You can build a dictionary with:
let allActions = ["runCentre":runCentre,"runLeft":runLeft,"runRight": runRight,"runTop":runTop,"runBottom":runBottom]
Now suppose you have a node with these possible actions:
let runDiag = SKAction.move(to: CGPoint(x:0.0,y:200.0), duration: 1.0)
let possibleActions = ["runCentre":runCentre, "runRight": runRight, "runDiag":runDiag]
To know what possibleActions are available in allActions (relying on equal keys) you could do:
let availableActions = allActions.filter { possibleActions.keys.contains($0.key) }

Save the list of all actions in a dictionary of type [String:SKAction].
let allActions = [
"runCenter": runCenter,
"runLeft": runLeft,
// etc
]
If you have a node's possible actions in a String array, you could use (flat)Map.
let possibleActions = ["runCenter", "idle"]
let possibleSKActions = possibleActions.flatMap { allActions[$0] }
// or if we are sure the action names will always have a value
let possibleSKActions = possibleActions.map { allActions[$0]! }

Related

How to turn NSManagedObject values into a Dictionary with key and value pairs?

I have Core Data setup in my application so that when there is no internet connection the app will save data locally. Once a connection is established it will trigger online-mode and then I need to send that data that I have stored inside Core Data up to my database.
My question is how do you turn an entity into a dictionary such as this:
<Response: 0x1c0486860> (entity: AQ; id: 0xd000000000580000 <x-coredata://D6656875-7954-486F-8C35-9DBF3CC64E34/AQ/p22> ; data: {
amparexPin = 3196;
dateFull = "\"10/5/2018\"";
finalScore = 34;
hasTech = No;
intervention = Before;
responseValues = "(\n 31,\n 99,\n 82,\n 150,\n 123,\n 66,\n 103,\n 125,\n 0,\n 14\n)";
timeStamp = "\"12:47\"";
who = "Luke";
})
into this:
amparexPin: 5123
timeStamp: 10:30
hasTech: No
intervention: Before
Basically a dictionary, I am trying to perform the same operation on each set of data on each entity. I know it sounds overly complicated but its quite imperative that each entity go through the same filter/function to then send its data up to a database. Any help on this would be awesome!
I see two ways to go here. The first is to have a protocol with some "encode" function, toDictionary() -> [String, Any?] that each managed object class implements in an extension and then call this function on each object before sending it.
The advantage of this way is that you get more precise control of each mapping between the entity and the dictionary, the disadvantage is that you need to implement it for each entity in your core data model.
The other way is to make use of NSEntityDescription and KVC to extract all values in one function. This class holds a dictionary of all attributes, attributesByName that could be used to extract all values using key-value coding. Depending on if you need to map the data type of the values as well you can get that from the NSAttributeDescription. (If you need to deal with relationships and more there is also propertiesByName).
A simplified example. (Written directly here so no guarantee it compiles)
static func convertToDictionary(object: NSManagedObject) -> [String, Any?] {
let entity = object.entity
let attributes = entity.attributesByName
var result: [String: Any?]
for key in attributes.keys {
let value = object.valueForKey: key)
result[key] = value
}
}
you can use this function
func objectAsDictionary(obj:YourObject) -> [String: String] {
var object: [String: String] = [String: String]()
object["amparexPin"] = "\(obj.amparexPin)"
object["timeStamp"] = "\(obj.timeStamp)"
object["hasTech"] = "\(obj.hasTech)"
object["intervention"] = "\(obj.intervention)"
return object
}

How to make shallow copy of array in swift

I have searched for a while but couldn't find reasonable answer for this. I want to add/remove objects in one array to make effect in 2nd array which points to first array.
class Person
{
var name:String = ""
}
var arr1:[Person] = [Person]()
let p1 = Person()
p1.name = "Umair"
let p2 = Person()
p2.name = "Ali"
arr1.append(p1)
arr1.append(p2)
var arr2 = arr1
print("\(arr1.count)") //"2\n"
print("\(arr2.count)") //"2\n"
arr1.removeFirst()
print("\(arr1.count)") //"1\n"
print("\(arr2.count)") //"2\n"
Why changing arr1 does not affect arr2. Please help me out to accomplish this.
Arrays are value types. When we copy them, each copy is independent of the other. This is a virtue in Swift. What you are trying to do requires references so that effects on one can be seen by others. Try this code. Create a class (reference type) containing your data. Now changes to the container can be seen in the other.
class Person
{
var name: String
init(_ name: String) {
self.name = name
}
}
let p1 = Person("Umair")
let p2 = Person("Ali")
class Container {
var people = [Person]()
init(people: [Person]) {
self.people = people
}
}
let arr1 = Container(people: [p1, p2])
let arr2 = arr1
print(arr1.people)
print(arr2.people)
arr1.people.removeFirst()
print(arr1.people)
print(arr2.people)
Even if you're using Swift, you can still use NSArray.
Per Apple's documentation,
NSArray is an object representing a static ordered collection, for use instead of an Array constant in cases that require reference semantics.
The only downside is you'll have to import Foundation. This isn't a problem if you're creating an iOS or Mac app, as you're depending on it already.

iOS Swift: How to find unique members of arrays of different types based on specific attributes

Goal: I have two different classes, and two arrays containing members of each class. Using Swift 2.0, I would like to find the unique members of one array compared to the other based on specific attributes of each class.
Example:
class A {
var name: String
init(name: String) {
self.name = name
}
}
class B {
var title: String
init(title: String) {
self.title = title
}
}
let aArray = [A(name:"1"), A(name:"2"), A(name:"3"), A(name:"4")]
let bArray = [B(title:"1"), B(title:"2"), B(title:"5")]
So, I'm looking for some operation between aArray and bArray which returns the 3rd and 4th element of aArray, because they are uniquely in aArray, where the basis of comparison is the attributes A.name and B.title.
Of course, reversing the order of the operation would pick out the 3rd element of bArray, because it is uniquely in bArray.
I know I can accomplish the goal straightforwardly using a simple for loop, but I was hoping for something more elegant and more optimized. But if a for loop is as fast or faster than anything fancier, I'm happy to use it just as well.
I'm not sure fancy or elegant this code is, but, we could do something like this:
let mappedArray = bArray.map { $0.title }
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
So when we want the unique elements from aArray, we first map the elements from bArray to get an array of the value we want to actually compare:
let mappedArray = bArray.map { $0.title }
mappedArray is just an array of strings based on the title property of the objects in bArray.
Next, we use the filter method to filter objects from aArray. The filter method returns an array with objects that pass the test in our closure. The test we want to apply is objects that are not contained in the mapped array we just built.
let filteredArray = aArray.filter { !mappedArray.contains($0.name) }
If we want to do it the other way, just change a few things:
let mappedArray = aArray.map { $0.name }
let filteredArray = bArray.filter { !mappedArray.contains($0.title) }

Store different item types in dictionary

I want to store a dictionary as:
[String : AnyItem]
because the format will be
["codename" : "title", "image" : UIImage("imageName")]
but I am having a difficult type accomplishing this. The ultimate goal is to have an array full of these types of dictionaries.
If you want a dictionary value that can hold anything, you need Any (rather than AnyType.
But this will be a real pain – whenever you want to get the value out, you’ll have to covert the types back every time, and if you mess up doing that, you’ll get all sorts of errors.
Instead, consider using a tuple – a pair of the two types you want. You can name the elements to make them easier to access:
var images: [(codename: String, image: UIImage)] = []
if let url = NSURL(string: "http://www.gravatar.com/avatar/24d29e1ff91b68642bbef96b43b5e119?s=128&d=identicon&r=PG&f=1"),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data) {
images.append(codename: "AlexAvatar", image: image)
}
images[0].codename // name of the first image in the array
images[0].image // the first image in the array
Alternatively, if you are going to want to address the images by name, why not use the “codename” field as the dictionary key?
var images: [String:UIImage)] = []
if let url = NSURL(string: "http://www.gravatar.com/avatar/24d29e1ff91b68642bbef96b43b5e119?s=128&d=identicon&r=PG&f=1"),
let data = NSData(contentsOfURL: url),
let image = UIImage(data: data) {
images["AlexAvatar"] = image
}
for (codename, image) in images {
// iterate over the codename/image pairs
}
If your actual data requirements are more complicated, consider a struct instead of a tuple.

Unable to create array of SKActions

I'm experimenting with SpriteKit in Swift but somehow seem unable to create an array of actions to use in a sequence. I've split it up to try and pin-point the problem, but no luck so far.
func animateBackground(){
let moveLeft = SKAction.moveByX(100, y: 0, duration: 3)
moveLeft.timingMode = SKActionTimingMode.EaseInEaseOut
let moveRight = SKAction.reversedAction(moveLeft)
let actions = [moveLeft, moveRight] // <--- here there be dragons/trouble
let sequence = SKAction.sequence(actions)
let repeat = SKAction.repeatActionForever(sequence)
}
When trying to create the actions-array I get the error "Cannot convert the expression's type 'Array' to type 'ArrayLiteralConvertible' " So, I thought I might need to be more explicit and attempted to change it to
var actions: SKAction[] = [moveLeft, moveRight]
This seemed to bring down the house, and not in a good way, resulting in the SourceKit terminated bug...
You're adding a function to the array for moveRight, not the SKAction itself. Try using this instead:
let moveRight = SKAction.reversedAction(moveLeft)()
When you create moveRight you're actually generating a function. You can call the function with "()" to get the actual SKAction. I added explicit types to the two SKAction's so it's clear that they can be put in an SKAction[]:
let moveLeft:SKAction = SKAction.moveByX(100, y: 0, duration: 3)
moveLeft.timingMode = SKActionTimingMode.EaseInEaseOut
let moveRight:SKAction = moveLeft.reversedAction()
let actions = [moveLeft, moveRight]
let sequence = SKAction.sequence(actions)
let repeat = SKAction.repeatActionForever(sequence)

Resources