Is there any way to have an n dimensional array in swift? I would like to be able to make a function that creates an array with n dimensions but I cannot figure out how.
Basically something like this:
func ndarray <T> (dimensions: Int...) -> [[T]] { // What do I tell it I return?
var out
for d in dimensions {
out = Array<T>(repeating: out, count: d)
}
return out
}
The above code does not work for obvios reasons but, I think it points out the main problems I am having:
How do I define a return type
How do I actually create the array
Once created how do I traverse and populate the array
Here is the implementation of an N-Dimensional Array. It uses a normal array internally for storage and converts the multi-dimensional indices into a single index for the internal array.
struct NDimArray<T> {
let dimensions: [Int]
var data: [T]
init(dimensions: Int..., initialValue: T) {
self.dimensions = dimensions
data = Array(repeating: initialValue, count: dimensions.reduce(1, *))
}
init(dimensions: Int..., initUsing initializer: () -> T) {
self.dimensions = dimensions
data = (0 ..< dimensions.reduce(1, *)).map { _ in initializer() }
}
// Compute index into data from indices
private func computeIndex(_ indices: [Int]) -> Int {
guard indices.count == dimensions.count else { fatalError("Wrong number of indices: got \(indices.count), expected \(dimensions.count)") }
zip(dimensions, indices).forEach { dim, idx in
guard (0 ..< dim) ~= idx else { fatalError("Index out of range") }
}
var idx = indices
var dims = dimensions
var product = 1
var total = idx.removeLast()
while !idx.isEmpty {
product *= dims.removeLast()
total += (idx.removeLast() * product)
}
return total
}
subscript(_ indices: Int...) -> T {
get {
return data[computeIndex(indices)]
}
set {
data[computeIndex(indices)] = newValue
}
}
}
Example:
// Create a 3 x 4 x 5 array of String with initial value ""
var arr = NDimArray<String>(dimensions: 3, 4, 5, initialValue: "")
for x in 0 ..< 3 {
for y in 0 ..< 4 {
for z in 0 ..< 5 {
// Encode indices in the string
arr[x, y, z] = "(\(x),\(y),\(z))"
}
}
}
// Show internal storage of data
print(arr.data)
["(0,0,0)", "(0,0,1)", "(0,0,2)", "(0,0,3)", "(0,0,4)", "(0,1,0)", "(0,1,1)", "(0,1,2)", "(0,1,3)", "(0,1,4)", "(0,2,0)", "(0,2,1)", "(0,2,2)", "(0,2,3)", "(0,2,4)", "(0,3,0)", "(0,3,1)", "(0,3,2)", "(0,3,3)", "(0,3,4)", "(1,0,0)", "(1,0,1)", "(1,0,2)", "(1,0,3)", "(1,0,4)", "(1,1,0)", "(1,1,1)", "(1,1,2)", "(1,1,3)", "(1,1,4)", "(1,2,0)", "(1,2,1)", "(1,2,2)", "(1,2,3)", "(1,2,4)", "(1,3,0)", "(1,3,1)", "(1,3,2)", "(1,3,3)", "(1,3,4)", "(2,0,0)", "(2,0,1)", "(2,0,2)", "(2,0,3)", "(2,0,4)", "(2,1,0)", "(2,1,1)", "(2,1,2)", "(2,1,3)", "(2,1,4)", "(2,2,0)", "(2,2,1)", "(2,2,2)", "(2,2,3)", "(2,2,4)", "(2,3,0)", "(2,3,1)", "(2,3,2)", "(2,3,3)", "(2,3,4)"]
print(arr[2, 2, 2]) // "(2,2,2)"
print(arr[3, 0, 0]) // Fatal error: Index out of range
print(arr[0, 4, 0]) // Fatal error: Index out of range
print(arr[2]) // Fatal error: Wrong number of indices: got 1, expected 3
Initializing an Array with a Reference Type
As #DuncanC noted in the comments, you have to be careful when initializing an array with a value which is a reference type, because the array will be filled with references to the object and modifying the object at any index will modify all of them.
To solve this, I added a second initializer:
init(dimensions: Int..., initUsing initializer: () -> T)
which takes a closure () -> T which can be used to create a new object for each element of the array.
For example:
class Person {
var name = ""
}
// Pass a closure which creates a `Person` instance to fill the array
// with 25 person objects
let arr = NDimArray(dimensions: 5, 5, initUsing: { Person() })
arr[3, 3].name = "Fred"
arr[2, 2].name = "Wilma"
print(arr[3, 3].name, arr[2, 2].name)
Fred Wilma
Nope, it's not possible. Array dimensions is something that needs to be determined at compile time, while the argument you want to pass to the initializer will not be known until runtime. If you really want to achieve something like this, then you'll need to move the array indexing from compile time to runtime, e.g. by accessing the array via an array of indexes. Still you don't have compile validation, since the array length can at runtime to not match the dimensions of the array.
This problem is similar to the one that attempts to convert a tuple to an array.
I am trying to check if array categories contain number 1 as Int since categories = [Int]() for example categories = {1, 2, 3, 4, 5}
I have tried the below code which gives me error Binary operator '==' cannot be applied to operands of type 'Any' and 'Int'
if categories.contains (where: {$0 == 1}) {
// 1 is found
}
also tried it without the where and brackets as below which gives me the same error
if categories.contains { $0 == 1 } {
// 1 is found
}
I tried using just the element as below which gives me error Missing argument label 'where:' in call
if categories.contains(1) {
// 1 is found
}
How can I do that?
It seems like your category array is of type Any
Ways to fix it
You can declare your array as an Int array
var categories: [Int]
OR
You can change the following piece of code
if categories.contains { $0 == 1 } {
// 1 is found
}
to
if categories.contains { ($0 as! Int) == 1 } {
// 1 is found
}
Note: This method might cause your app to crash if your category array has an element other than of type Int
it is working See my output in PlayGround
Code used:
var categories : [Int] = [0,1,2,3,4,5,6,7,8,9]
if categories.contains(5)
{
print("Yes it contains")
}
else
{
print("it do not")
}
and also This condition is working
if categories.contains (where: {$0 == 1}) {
print("yes")
}
see your Array Declaration I think there is main Issue
Declaration 1 :
var categories = [Int]()
categories = [0,1,2,3,4,5,6,7,8,9]
Declaration 2 :
var categories : [Int] = [0,1,2,3,4,5,6,7,8,9]
Regarding error message
Binary operator '==' cannot be applied to operands of type 'Any' and 'Int'
Your array is not an Int array instead it contains Any so it needs typecasting before comparision. Declaration of array is also wrong use [] instead of {}. And typecast object as an int ($0 as! Int) == 1 (I'm using force casting here because I know its an Int array).
There are many ways to check if array contains any element.
1> Just try to get the index of element with guard if index is nil means array doesn't contain the element. Although you didn't declare array in right way still I'm considering it a valid array.
let categories: [Int] = [1, 2, 3, 4, 5]
guard categories.index(of: 1) != nil else {
print("Doesn't Contain")
return
}
print("Contains")
2> Use contains method
if (categories.contains(1)) {
print("Contains")
}
else {
print("Doesn't Contain")
}
3> Not Recommended for this case But still you can get this
let result = categories.filter({$0 == 1})
if result.count == 0 {
print("Doesn't Contain")
}
else {
print("Contains")
}
filter returns an array of element which matches with condition. So that if there are multiple 1 in array so it will give you an array of all elements. And $0 describes the object while enumerating the array.
4> Not Recommended for this case
let contains = categories.contains(where: {$0 == 1})
if contains {
print("Contains")
}
else {
print("Doesn't Contain")
}
Thanks to your comments made me check the declaration of the array and the problem was that was declared as [Any] after I get it's value from the UserDefaults. I have checked and found the solution on How do I save an Int array in Swift using NSUserDefaults?
// old declaration
let categories = userDefaults.array(forKey:"categories") ?? [Int]()
// new correct declaration
var categories = [Int]()
if let temp = userDefaults.array(forKey:"categories") as? [Int] {
categories = temp
}
So I am making a 2d-array like this:
var kcalVerdier:Array = new Array(92,80,103,36,53);
var alleNumSteppers:Array = new Array(alleNumSteps.numStepMelk.value,alleNumSteps.numStepEgg.value,alleNumSteps.numStepBrød.value,alleNumSteps.numStepSmør.value,alleNumSteps.numStepOst.value);
var c:Array = new Array(kcalVerdier,alleNumSteppers);
function endreAntall(evt:Event)
{
txtTotalKcal.text = String(c[0] * [0]);
}
Is it not possible to do multiply 2 values of a 2-d Array? I get this error:
Scene 1, Layer 'script', Frame 1, Line 17, Column 38 1067: Implicit coercion of a value of type Array to an unrelated type Number.
I don't understand why, c[0][0] should both be integer values or am I misunderstanding?
Which values you actually want to multiply?
By doing:
c[0] * [0]
you're trying to multiply Array by Array.
c[0] would be 1st element of c Array which is in fact kcalVerdier Array.
[0] is making new Array with one element (that is 0);
so it's like
[92,80,103,36,53] * [0]
[EDIT]
Ok, try this piece of code:
// Check if both Arrays are what we want:
trace("kcalVerdier => " + c[0]);
trace("alleNumSteppers => " + c[0]);
trace();
// Gett Arrays lengths
var arr1Length:int = c[0].length;
var arr2Length:int = c[1].length;
// Check if both are the same length
if(arr1Length == arr2Length)
{
// Let's iterate
for(var i:int = 0; i<arr1Length; i++)
{
trace( c[0][i] * c[1][i] );
}
}