Swift: how to reserveCapacity() for SKSpriteNodes - arrays

I am trying to allocate sprite data to a global array using preset indexes for the sprites. I am initializing the array as an array of SKSpriteNodes. I am sending SKSpriteNodes to this array, each sprite has a set index for this array. I realize I could also do this with a loop instead of setting indexes, but I want to figure out the array allocation first.
I have tried reserveCapacity(27) because there will be 27 sprites to pass in, but when I try I get an index out of range error.
class GameScene: SKScene
{
//main array that will be used to store sprite button data
var mainArr: [SKSpriteNode] = [SKSpriteNode]()
..
override func didMove(to view: SKView)
{
mainArr.reserveCapacity(27)
...
if let name = touchedNode.name
{
if name == "pea"
{
peaFlag = peaFlag * -1
manageArrayData(name: pea, nameFlag: peaFlag, nameIndex: peaInd)/*may need to add images*/
}
...}//end touchNode
...} //end didMoveTo
func manageArrayData(name: SKSpriteNode, nameFlag: Int, nameIndex: Int)
{
if nameFlag >= 0
{
print(nameFlag)
print(nameIndex)
print("in array")
mainArr.insert(name, at: nameIndex)
//dump(mainArr)
print("-=-=-=-in-=-=-=-")
}
as I said, the error is: Fatal error: Array index is out of range
2019-06-27 09:54:09.414271-0700 Select[36307:1432579] Fatal error: Array index is out of range
I believe the error is because reserveCapacity() is of type Int, while I am trying to allocate memory for SKSpriteNode... therefore there is no space for what I am allocating, hence the "out of range"
there are multiple "buttons" (using SKSpriteNodes), so I created an if tree for the buttons to fall under.

reserveCapacity does not create entries in the array. It just makes sure you won't need to perform a reallocation (and possible relocation) if you add that many entries. It's generally only needed for performance reasons. It won't change anything about how indexing works.
If you want empty entries in the array, you need to add them. See Array(repeating:count:) to create an array with some fixed values.

Related

C# - Tuple arrays are mutable, but tuple lists are not. How do I get around this?

I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}

Multidimensional array of Objects in Kotlin

I'm new in Kotlin, and I want to create a multi dimensional array of a custom class, with null permitted. Something like that
private var array_map = arrayOf<Array<Obstacle?>>()
...
array_map[1][2] = Obstacle()
How can I do it? Thank you!
In case you need the index of each element in the constructor of the elements of the array:
Declaration:
var matrix: Array<Array<Obstacle?>>
Instantiation and initialization:
matrix = Array(numRows) { row ->
Array(numCols) { col ->
Obstacle(row, col)
}
}
You can use private var arrayMap: Array<Array<Obstacle?>> = arrayOf(). Just wrap with as much Array<> as you need.
Not sure if this is what you want, but imagine that Obstacle is a custom class with a field num as below
data class Obstacle(var num: Int){}
A 2D array of the Obstacle object would be as below:
val array: Array<Obstacle?> = arrayOf(Obstacle(123), Obstacle(234))
val arrayOfArray: Array<Array<Obstacle?>> = arrayOf(array)
println(arrayOfArray[0][0]) // would print Obstacle(num=123)
println(arrayOfArray[0][1]) // would print Obstacle(num=234)
So you should be declaring your 2D array as below
val arrayOfArray: Array<Array<Obstacle?>> = arrayOf()
Your code will compile as is. The problem is just that array size can't be changed and arrayOf<Array<Obstacle?>>() creates an empty array, so array_map[1][2] = Obstacle() fails at runtime. (Unless you do array_map = ... somewhere between them. Note that you should prefer val arrayMap, which can't be reassigned, unless you have a specific reason to use var.)
If you want your array to start with nulls, there is arrayOfNulls in the standard library, but it only creates a single-dimensional array, and what you really need is an array of arrays of nulls. You can write a helper function:
inline fun <reified T> matrixOfNulls(n: Int, m: Int) = Array(n) { arrayOfNulls<T>(m) }
private val arrayMap = matrixOfNulls<Obstacle>(5, 5) // example arguments
The approach I always use for this case is:
arr2D = Array(sizeA) { Array(sizeB) { content } }
Note I replaced the sizes by fields names to illustrate that each number/field represents the width and height length of each dimension of the 2D array.
Also, content should be replaced by the main content you want to fill in each coordinate, in your case seems you aims to setup with Obstacle() instances. If you want fill this content in other moment put null or a quick Any() reference.
In this last case, after creating that you can simply perform to set the itens:
arr2D[1][2] = Obstacle()

2D arrays: Index out of range

I'm pretty new to Swift, and I'm trying to store my data as a 2D AnyObject array. I have declared var internalData = [[AnyObject]]() and a struct like so:
struct WiFiData {
var latency: Double
var duration: Double
}
Now, in another function, I would like to switch on the instance variable currentExit, which is an enum:
private var currentExit = Exit.A
enum Exit {
case A
case B
case C
case NotSelected
}
func someFunc() {
switch self.currentExit {
case .A:
self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) // <- CRASHES ON THIS LINE
......// other cases
}
However, it always crashes on the line self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) with the following error:
fatal error: Index out of range
Can anyone tell me why and how I could fix it? Any help is appreciated, thanks!
You instantiated a 2d array with [[AnyObject]](), but it's still empty, meaning there's nothing at self.internalData[0] for you to append to. There are a lot of things you could do to handle this, but if you know you're going to have 3 arrays inside self.internalData you may as well instantiate each internal array like so:
self.internalData = [[AnyObject](), [AnyObject](), [AnyObject]()]
Now you do have an empty array at self.internalData[0] that you can append items to.
It seems, though, like if you already know you're going to have 3 arrays of WiFiData it'd be even better just to create 3 variables:
var a = [WiFiData]()
var b = [WiFiData]()
etc.
and then you can access the individual arrays by name.
There are no "2D arrays" in Swift. There are arrays of arrays, which are different. For example, there is no promise that each row will have the same number of columns in an array of arrays.
In your case, the problem is that internalData has no elements in it. There has to be an internalData[0] element already created before you can append to it. You're assignment of var internalData = [[AnyObject]]() means there's no element 0. It's an empty array (of arrays).
Also keep in mind that the type [[AnyObject]] is very likely to cause a lot of problems for you. AnyObject is useful for working with Cocoa APIs, but generally creates a lot of headaches and should very rarely be part of a Swift property. You should almost certainly create some more specific type to hold your data. It's unclear from your example what you expect internalData to hold. Your code suggests, though, that you mean something more like:
var internalData: [Exit: [ExitRecord]] = [:]
enum Exit {
case A
case B
case C
case NotSelected
}
enum ExitRecord {
case wifi(latency: Float, duration: Float)
}
func someFunc() {
switch self.currentExit {
case .A:
var currentRecords = internalData[.A] ?? []
currentRecords.append(.wifi(latency: 1.5, duration: 4.0))
internalData[.A] = currentRecords
......// other cases
}

Getting the indexes of NSButtons in an array and appending them to an array of integers in swift (Cocoa)

I'm super new to programming so this may be a pretty basic question.
I have an array of NSButtons (checkboxes) in my ViewController that I am calling "buttonArray." I want to go through the array, find out which buttons are checked and add the indexes of those checked buttons to another array of integers (located in a struct), which I am calling "intArray." This is the struct:
struct GetInterval {
var intArray = [Int]()
mutating func putIntIntoArray (intToAdd:Int) {
self.intArray.append(intToAdd)
}
}
I have tried doing this two ways (shown below), both of which have given me compiler errors.
First, I tried to use a function to import the index as an Int and add it to the "intArray" array...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.putIntIntoArray(Int(intervalIndex!))
}
}
...which gave me the error: "Cannot invoke 'putIntoArray' with argument list of type ((int))"
When that didn't work, I tried appending it directly from the "if" statement in the ViewController...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.intArray.append(intervalIndex!)
}
}
...which gives me the error: "GetInterval.Type does not have a member named 'intArray'"
How can I fix this?
When you say GetInterval.(something), this refers to a static member (a.k.a., something owned by the GetInterval type). However, your intArray is a member owned by each instance of the GetInterval type. So you need to create an instance first. For example:
var getInt = GetInterval() // create a new instance
getInt.putIntIntoArray(...) // modify this instance, by calling a mutating function
getInt.intArray.append(...) // modify this instance, by directly accessing a property

Search a struct nested in an array

I have a struct that's inside an array that is nested inside another struct, like so: Arguments.cart.data.Items[x].Labels.Pkg.Title (x is an index, as I'm looping over Items).
Items is an array while Labels, Pkg, and Title are nested structs.
Title does not always exist. So I would like to check for it. However, using structFindKey returns an error
You have attempted to dereference a scalar variable of type class coldfusion.runtime.Array as a structure with members
I could just look inside Arguments.cart.data; however, if there are multiple rows in the array, some rows may contain Title while others do not. So I want to check for Title inside each Items.
I've also tried arrayFind, but then I get the error
Struct cannot be used as an array
I'm at a loss here.
This will do the job
<cfscript>
for (i=1;i<=ArrayLen(arguments.cart.data.Items);i++) {
tempI = arguments.cart.data.Items[i];
if (IsDefined('tempI.Labels.Pkg.Title')) {
// It exists
} else {
// It doesn't
}
}
</cfscript>
IsDefined doesn't play nicely with arrays, but by assigning each element of the array to the temp value you are then able to refer to it within IsDefined.
Alternatively you can do the following, if you prefer StructKeyExists
<cfscript>
for (i=1;i<=ArrayLen(arguments.cart.data.Items);i++) {
tempI = arguments.cart.data.Items[i];
if (
StructKeyExists(tempI,'Labels')
&& StructKeyExists(tempI.Labels,'Pkg')
&& StructKeyExists(tempI.Labels.Pkg,'Title')
) {
// It exists
} else {
// It doesn't
}
}
</cfscript>
I've come across this in the past too. Just stick your array into a struct temporarily... this will trick structFindKey() and structFindValue() into working properly.

Resources