Swift Dictionary: Get values as array - arrays

I have a dictionary containing UIColor objects hashed by an enum value, ColorScheme:
var colorsForColorScheme: [ColorScheme : UIColor] = ...
I would like to be able to extract an array of all the colors (the values) contained by this dictionary. I thought I could use the values property, as is used when iterating over dictionary values (for value in dictionary.values {...}), but this returns an error:
let colors: [UIColor] = colorsForColorSchemes.values
~~~~~~~~~~~~~~~~~~~~~^~~~~~~
'LazyBidrectionalCollection<MapCollectionView<Dictionary<ColorScheme, UIColor>, UIColor>>' is not convertible to 'UIColor'
It seems that rather than returning an Array of values, the values method returns a more abstract collection type. Is there a way to get an Array containing the dictionary's values without extracting them in a for-in loop?

As of Swift 2.0, Dictionary’s values property now returns a LazyMapCollection instead of a LazyBidirectionalCollection. The Array type knows how to initialise itself using this abstract collection type:
let colors = Array(colorsForColorSchemes.values)
Swift's type inference already knows that these values are UIColor objects, so no type casting is required, which is nice!

You can map dictionary to an array of values:
let colors = colorsForColorScheme.map { $0.1 }
Closure takes a key-value tuple from dictionary and returns just a value. So, map function produces an array of values.
More readable version of the same code:
let colors = colorsForColorScheme.map { (scheme, color) in
return color
}
UPDATE
From Xcode 9.0, dictionary values can be accessed using values property, which conforms to Collection protocol:
let colors = colorsForColorScheme.values
Typically you just want it as an array:
let colors = Array(dict.values)
and that's it.

Use colorsForColorScheme.map({$0.value})

you can create an extension on LazyMapCollection
public extension LazyMapCollection {
func toArray() -> [Element]{
return Array(self)
}
}
colorsForColorSchemes.values.toArray() or colorsForColorSchemes.keys.toArray()

Firstly, from the following statement, it seems that your variable(dictionary) name is colorsForColorScheme
var colorsForColorScheme: [ColorScheme : UIColor] = ...
while you are trying to get the values from colorsForColorSchemes dictionary when you did-
let colors: [UIColor] = colorsForColorSchemes.values
which should give you a compile time error. Anyways I am assuming that you had a typo, and you dictionary's name is colorsForColorSchemes. So, here is the solution-
As mentioned earlier, because of the type inference property in swift, your code can infer that the returned type from the .values function is returning an array of UIColor. However, Swift wants to be type-safe, so when you store the values in the colors array, you need to explicitly define that. For swift 5 and above, now you could just do following-
let colors = [UIColor](colorsForColorSchemes.values)

You can also use flatMap:
let colors = colorsForColorScheme.values.flatMap { $0 }

I've found this to be the most useful in Swift 5:
colorsForColorSchemes.allValues
See docs - https://developer.apple.com/documentation/foundation/nsdictionary/1408915-allvalues

Related

Getting the first index of an array of objects using a property - swift

I have an array of objects called modelArray these objects have a property which is called timestamp which is of type FIRTimestamp.
Knowing that we have another timestamp which we are gonna call comparingTimestamp
I'm trying to figure out how I can find the first index in the array where the property timestamp is > of the comparingTimestamp.
If you are not practical with firebase, the object Timestamp has a numerical property called seconds which we can use for making the comparison.
Until now I tried using the function where but without any good result.
You can use collection's firstIndex(where:) method:
struct Item {
let timestamp: FIRTimestamp
}
let modelArray = [item1, item2, item3, item4]
let firstIndex = modelArray.firstIndex { item in
item.timestamp.seconds > comparingTimestamp.seconds
}
print(firstIndex!)

SWIFT: assigning all values of an item in a structure array to a variable

Sorry, I don't even have an idea of the keywords to search for answer.
I want to store all items within a global structure to a local variable.
struct HighScores: Codable {
var highscoreRecord: [HighscoreRecord]
}
struct HighscoreRecord: Codable {
var Rank:Int
var Date:Date
var avDuration:Float
var Score:Int
}
A global variable is based on this structure and populated within a UIViewController
var jsonResult: HighScores?
Now, in another UIViewController, I want to extract the values of Score for all Highscores and store it to a local variable. I thought it should look somewhat like this, however, I do not get it to work
#IBDesignable class ScoreTimeGraphView: UIView {
var graphScore = jsonResult!.highscoreRecord.Score
The declaration above throws "Value of type '[HighscoreRecord]' has no member 'Score'"
Any ideas how to do this?
Cheers!
highscoreRecord is an Array. You can't use .Score directly on it because the Array type doesn't have a property named Score.
However, because it's element type is HighScore (which does have the property you want), you can iterate over it and collect the Score property from each one.
I think this is what you are after:
var allGraphScores = jsonResult!.highscoreRecord.map { $0.Score }
.map(_:) takes a closure with one parameter, and passes in each element of a sequence in turn.
So, highscoreRecord.map { $0.Score } returns a new array, by finding the Score property of each HighScoreRecord in the array highscoreRecord.
PS it's probably a good idea to name your variables using lowercase camelCase, for readability and instant recognition by any Swift dev that Score is a variable and not an object.
You're trying to access the property from an array. You need to provide an index to remove the error. Update this line:
var graphScore = jsonResult!.highscoreRecord.Score
To this:
var graphScore = jsonResult!.highscoreRecord[0].Score

Access a value in a dictionary using an int variable in Swift

I'm developing an iOS app and I want to access a specific value in a Dictionary using Array().
My dictionary contains an array, which contains structs.
let array = [(key: "S", value: [Thunderbolt.repoStruct(repoName: "Semiak Repo", repoURL: "https://repo.semiak.dev", icon: Optional("iconRound"))]), (key: "T", value: [Thunderbolt.repoStruct(repoName: "Thunderbolt iOS Utilities", repoURL: "https://repo.thunderbolt.semiak.dev", icon: Optional("iconRound"))])]
I'm making an UITableView with the array: the section name is the key value, the cell title is the repoStruct.repoName value, and the same with the following values.
To access repoName I'd use Array(array)[0].1[0].repoName.
The problem is that I do not know the exact location I want to access, instead, I use indexPath to know which value I need:
Array(array)[indexPath.section].indexPath.row[0].repoName
This should return me the repoName of the cell, but instead gives me the following error: Value of tuple type '(key: String, value: [repoStruct])' has no member 'indexPath'
I also tried using:
let row = indexPath.row
Array(array)[indexPath.section].row[0].repoName
but it gives me the same error: Value of tuple type '(key: String, value: [repoStruct])' has no member 'row'
I do not know why Array(array)[0].1 works and returns me the value, but Array(array)[indexPath.section].row doesn't. It is doing the same: accessing a value using the position, which is an int, such as indexPath.
How could I accomplish this?
Thanks in advance.
You are strongly discouraged from using tuples in a data source array. Replace the tuple with an extra struct
struct Section {
let name : String
let items : [Thunderbolt.repoStruct]
}
let array = [Section(name: "S", items: [Thunderbolt.repoStruct(repoName: "Semiak Repo", repoURL: "https://repo.semiak.dev", icon: Optional("iconRound"))],
Section(name: "T", items: [Thunderbolt.repoStruct(repoName: "Thunderbolt iOS Utilities", repoURL: "https://repo.thunderbolt.semiak.dev", icon: Optional("iconRound"))]]
and get an item at index path
let section = array[indexPath.section]
let item = section.items[indexPath.row]
let name = item.repoName
First of all, your array is already an array, so there's no need to say Array(array) - simply array will suffice, although generic names like this should be avoided.
I do not know why array[0].1[0] works
Let's pick this apart - your're accessing the first element in array via [0] and within that, the second element of the tuple .1, and lastly the first element of that valuearray. You could use array[0].value[0] for the same effect and make the code more readable.
but array[indexPath.section].row doesn't
That's because your array does not contain anything called row.
Use array[indexPath.section].value[indexPath.row].repoName instead.
Please try this code.
let dictData = arr[indexpath.section] //Element of section
let value = dictData["value"] //Value added in value in The element
let name = value[indexpath.row].reponame //Gives you name

Swift 3: Dictionary from large arrays

I am working with two large arrays containing following data:
print(dataID[1]) // ["DataID123"]
print(dataAR[1]) // ["73.075584"]
I'd like to form a dictionary from the two arrays by:
var arrayofDict = [String: AnyObject?]()
for i in 0...csvDataID.count {
if i < csvDataID.count {
let key = csvDataID[i]
let value = csvDataAG[i]
arrayofDict[key] = value
}
}
But get following error:
Cannot subscript a value to '[String: AnyObject?]' with an index of
type '[String]'
I also tried a few other approaches aswell resulting in the same error.
Could anyone help me and is there even a more efficient way to handle the arrays?
Thanks!
let key = csvDataID[i][0]
let value = csvDataAG[i][0]
Explaination:
The log says that csvDataID[1] is an array: ["DataID123"]
The same is for the second one.:["73.075584"]
So,
let key = csvDataID[i] // you set the key is array.
That's why you can see this error.

Modifying an array of dictionaries in Swift

I’m new to Swift and have been having some troubles figuring out some aspects of Arrays and Dictionaries.
I have an array of dictionaries, for which I have used Type Aliases - e.g.
typealias myDicts = Dictionary<String, Double>
var myArray : [myDicts] = [
["id":0,
"lat”:55.555555,
"lng”:-55.555555,
"distance":0],
["id":1,
"lat": 44.444444,
"lng”:-44.444444,
"distance":0]
]
I then want to iterate through the dictionaries in the array and change the “distance” key value. I did it like this:
for dict:myDicts in myArray {
dict["distance"] = 5
}
Or even specifically making sure 5 is a double with many different approaches including e.g.
for dict:myDicts in myArray {
let numberFive : Double = 5
dict["distance"] = numberFive
}
All my attempts cause an error:
#lvalue $T5' is not identical to '(String, Double)
It seems to be acting as if the Dictionaries inside were immutable “let” rather than “var”. So I randomly tried this:
for (var dict:myDicts) in myArray {
dict["distance"] = 5
}
This removes the error and the key is indeed assigned 5 within the for loop, but this doesn't seem to actually modify the array itself in the long run. What am I doing wrong?
The implicitly declared variable in a for-in loop in Swift is constant by default (let), that's why you can't modify it directly in the loop.
The for-in documentation has this:
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
In the example above, index is a constant whose value is automatically
set at the start of each iteration of the loop. As such, it does not
have to be declared before it is used. It is implicitly declared
simply by its inclusion in the loop declaration, without the need for
a let declaration keyword.
As you've discovered, you can make it a variable by explicitly declaring it with var. However, in this case, you're trying to modify a dictionary which is a struct and, therefore, a value type and it is copied on assignment. When you do dict["distance"] = 5 you're actually modifying a copy of the dictionary and not the original stored in the array.
You can still modify the dictionary in the array, you just have to do it directly by looping over the array by index:
for index in 0..<myArray.count {
myArray[index]["distance"] = 5
}
This way, you're sure to by modifying the original dictionary instead of a copy of it.
That being said, #matt's suggestion to use a custom class is usually the best route to take.
You're not doing anything wrong. That's how Swift works. You have two options:
Use NSMutableDictionary rather than a Swift dictionary.
Use a custom class instead of a dictionary. In a way this is a better solution anyway because it's what you should have been doing all along in a situation where all the dictionaries have the same structure.
The "custom class" I'm talking about would be a mere "value class", a bundle of properties. This was kind of a pain to make in Objective-C, but in Swift it's trivial, so I now do this a lot. The thing is that you can stick the class definition for your custom class anywhere; it doesn't need a file of its own, and of course in Swift you don't have the interface/implementation foo to grapple with, let alone memory management and other stuff. So this is just a few lines of code that you can stick right in with the code you've already got.
Here's an example from my own code:
class Model {
var task : NSURLSessionTask!
var im : UIImage!
var text : String!
var picurl : String!
}
We then have an array of Model and away we go.
So, in your example:
class MyDict : NSObject {
var id = 0.0
var lat = 0.0
var lng = 0.0
var distance = 0.0
}
var myArray = [MyDict]()
let d1 = MyDict()
d1.id = 0
d1.lat = 55.55
d1.lng = -55.55
d1.distance = 0
let d2 = MyDict()
d2.id = 0
d2.lat = 44.44
d2.lng = -44.44
d2.distance = 0
myArray = [d1,d2]
// now we come to the actual heart of the matter
for d in myArray {
d.distance = 5
}
println(myArray[0].distance) // it worked
println(myArray[1].distance) // it worked
Yes, the dictionary retrieved in the loop is immutable, hence you cannot change.
I'm afraid your last attempt just creates a mutable copy of it.
One possible workaround is to use NSMutableDictionary:
typealias myDicts = NSMutableDictionary
Have a class wrapper for the Swift dictionary or array.
class MyDictionary: NSObject {
var data : Dictionary<String,Any>!
init(_ data: Dictionary<String,Any>) {
self.data = data
}}
MyDictionary.data

Resources