1
votes

I've been racking my brain for hours and feel like I've tried most things so I'm turning to the trusty Stack Overflow!

I have an array of arrays coming from a server that correlates to a unix timestamp and a value which looks like this:

[[1475511843000,183649999],[1475512143000,183612691],[1475512443000,183503638]]

I used to simply set this variable up as var graphPoints:NSArray = []

I'm now trying to convert to Swift3 and use [[UInt32:Int]]() to specify that I have an array containing other arrays which have a 32 Unsigned Integer as well as an Integer afterwards. No matter which way I've tried including standard vs. sugared syntax:

[Int:[UInt32:Int]]()

[[UInt32:Int]]()

[[]]()

allows me to access the second array due to a typing issue, normally giving an error akin to Type 'Int?' has no subscript members.

The data was in this format before I had a say as to how it would be formatted so I'm not able to adjust that, any help would be great appreciated.

The server call looks like this: `Alamofire.request(urlString).responseJSON { response in switch response.result { case .success(let data): let json = JSON(data);

            //print("GRAPH DATA POINTS JSON: \(json)")

            //some graphs don't contain data so we have to test for null
            if !json.isEmpty {
                self.graphPoints = json["data"].arrayObject! as! [(timestamp: UInt64, value: Int)]
                if self.graphPoints.count > 0 {
                    self.firstPriceInGraph = self.graphPoints[0][0].doubleValue
                    self.setupPercentageLabel()
                } else {
                    //no graph data available, hide something?
                    self.firstPriceInGraph = -1.0
                    if Helper.__DEBUG { print("NO DATA IN JSON... 1")}
                }
            } else {
                //no graph data available, hide something?
                self.firstPriceInGraph = -1.0
                if Helper.__DEBUG {print("NO DATA IN JSON... 2")}
            }

            self.graphView.reloadData()

        case .failure(let error):
            self.graphPoints = [(timestamp:UInt64, value:Int)]()
            self.graphView.reloadData()
            print("Get Graph Data - Request failed with error: \(error)")
        }
    }`

The Error

When trying Andrey Gordeev's answer Initial Array Setup

enter image description here

4
You have array of array and in first two declaration you are telling that it is Array of dictionary try once [[UInt32]] - Nirav D
@NiravD 1475511843000 doesn't suite UInt32. UInt64 is a better choice here. - Andrey Gordeev
Looks like OP just wants to get the old Swift 2.2 code working just fine with Swift 3.0 :) Unfortunately, sometimes it's impossible. - Andrey Gordeev
@StuartP. that makes sense. See my updated answer. - Andrey Gordeev
Actually, I don't think this helps. There is a problem with graphPoints and firstPriceInGraph: they have wrong types - Andrey Gordeev

4 Answers

1
votes

Just as you said, this is an Array of other Arrays. [[UInt32:Int]]() would create an Array of Dictionarys instead because [UInt32:Int] is a Dictionary with UInt32 as keys and Int as values.

Swift arrays only hold a single type of element, so you can do [[Integer]], Integer being a protocol that both UInt32 and Int conform to. Then when accessing you would have to explicitly cast to either UInt32 or Int if you wanted them in those types.

You could also then convert that [[Integer]] to a [(UInt32, Int)], an Array of tuples containing a UInt32 and an Int.

1
votes

I'm now trying to convert to Swift 3 and use [[UInt32:Int]]() to specify that I have an array containing other arrays which have a 32 Unsigned Integer as well as an Integer afterwards.

That's not what at all what that means. [[UInt32: Int]]() is the shorthand for: Array<Dictionary<UInt32, Int>>.init()

The only way to have an array of 2 types, such as UInt32 and Int, is to have the array store a super type of both of those types. In this case, UInt32 and Int both share a conformance to Integer protocol, so you can store both UInt32 and Int in an Array<Integer>. This is pretty gross though, as it'll require you to constantly explicitly down cast from Integer to UInt32 or Int. The indirection that this introduces also has performance costs.

A better way of achieving a similar thing is to store tuples of UInt32 and Int:

let graphPoints: [(UInt32, Int)] = [
    (1475511843000, 183649999),
    (1475512143000, 183612691),
    (1475512443000, 183503638)
]

However, this doesn't even work, because 1475511843000 is too big to stope in a UInt32, whose max value is 4294967295 (UInt32.max).

1
votes

What you wrote is a dictionary, not a multi dimensional array.

[[UInt32:Int]]()

creates an array of dictionaries that has UInt32 as keys and Int as values.

[Int:[UInt32:Int]]()

creates a dictionary that uses Int as keys and another dictionary type as values.

Both of the above is not what you want.

Since your data are UInt32 - Int pairs, you probably shouldn't use a multidimensional array to store them because an array can only store one type of value.

My suggestion is to use an array of tuples:

[(UInt32, Int)]

Example usage:

theArray[3].0 // get the timestamp of the fourth item
theArray[7].1 // get the Int value of the eighth item

Alternatively, try a dictionary:

[UInt32: Int]

Note that this makes the data unordered. You can only access the values using the keys.

1
votes

How about this:

let array = [[UInt64]]() 

or

let array: [[UInt64]] = [[1475511843000,183649999],[1475512143000,183612691],[1475512443000,183503638]]

What you're trying to define by [Int:[UInt32:Int]]() and [[UInt32:Int]]() is not an array of arrays. First one is a dictionary, where value is another dictionary. Second one is an array of dictionaries.

PS: As Alexander Momchilov mentioned, it's better to wrap your data to [(UInt64, Int)]