Swift - Struct or Dictionary
Just to clarify one point when you are worried about memory resources by using struct
. A structure is value copied passed type. It's not stored in the memory heap unlike classes (Reference types).
So go and use struct
, it's recommended by Apple itself.
Dictionary of structs in a struct
You have a few things going on here. For starters, you are initialising an empty Dict not an empty Array of dictionaries. Your property within your Thingy is a let which is a constant. Like mentioned earlier adding to things needs to be in a function inside a Struct and if you are changing that property you are mutating it.
struct Thing{
let title: String
}
struct Thingy {
var things = [[String:Thing]]()
mutating func createSomeThings() {
let thingo = ["thing1" : Thing(title:"stuff")]
things.append(thingo)
}
mutating func addAnotherThingWith(name: String, title: String) {
let thingo = [name : Thing(title:title)]
things.append(thingo)
}
}
There are to ways you can achieve your goal.
Swift: Hashable struct with dictionary property
I think you need to review your data model if you have to use a whole struct as a dictionary key. Anyhow, here's one way to do it:
internal struct MapKey: Hashable {
internal let id: String
internal let values: [String:String]
var hashValue: Int {
get {
var hashString = self.id + ";"
for key in values.keys.sort() {
hashString += key + ";" + values[key]!
}
return hashString.hashValue
}
}
}
func ==(lhs: MapKey, rhs: MapKey) -> Bool {
return lhs.id == rhs.id && lhs.values == rhs.values
}
This assumes that you don't have semicolon (;
) in id
or in the keys and values of values
. Hasable
implies Equatable
so you don't need to declare it conforming to Equatable
again.
Is it possible to use a Type as a dictionary key in Swift?
Is it possible to use a Type as a dictionary key in Swift?
Well its possible, here is one way:
protocol Growable { ... }
struct S : Growable { ... }
class C : Growable { ... }
extension Dictionary where Key : LosslessStringConvertible
{
subscript(index: Growable.Type) -> Value?
{
get
{
return self[String(describing: index) as! Key]
}
set(newValue)
{
self[String(describing: index) as! Key] = newValue
}
}
}
var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
print(d)
prints:
["C": 24, "S": 42]
If you change the subscript
definition to:
subscript(index: Any.Type) -> Value?
you can of course use any type as a key:
var d : [String:Int] = [:]
d[S.self] = 42
d[C.self] = 24
d[type(of:d)] = 18
print(d)
prints:
["C": 24, "S": 42, "Dictionary<String, Int>": 18]
I'll leave it up to you to decide whether this is wise, but its clearly possible.
[Note: you cannot constrain Key
to be String
hence the use of the protocol LosslessStringConvertible
; there might be a better choice, the Swift standard library is a moving target...]
HTH
Should a dictionary be converted to a class or struct in Swift?
To access it like this you need to convert your dictionary to Struct as follow:
edit/update: Swift 3.x
struct Job: CustomStringConvertible {
let number: Int
let name, client: String
init(dictionary: [String: Any]) {
self.number = dictionary["jobNumber"] as? Int ?? 0
self.name = dictionary["jobName"] as? String ?? ""
self.client = dictionary["client"] as? String ?? ""
}
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
let job = Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions"""
edit/update:
Swift 4 or later you can use JSON Codable protocol:
struct Job {
let number: Int
let name, client: String
}
extension Job: Codable {
init(dictionary: [String: Any]) throws {
self = try JSONDecoder().decode(Job.self, from: JSONSerialization.data(withJSONObject: dictionary))
}
private enum CodingKeys: String, CodingKey {
case number = "jobNumber", name = "jobName", client
}
}
extension Job: CustomStringConvertible {
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
do {
let job = try Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions\n"
} catch {
print(error)
}
Swift: Filter a dictionary key from a struct from an array, which is optional
It's not completely clear to me what you're attempting to do, however, this will filter your cases
array to only Test
objects that contain non-nil values
in the message dictionary:
let nonNil = cases.filter { (test) -> Bool in
return Array(test.message.values).filter({ (value) -> Bool in
return value == nil
}).count <= 0
}
The variable nonNil
now contains the Test
objects where title is "2" and title is "4".
You could further filter that if you want a [tags:preview]
dictionary. Something like this would do that:
let tags = nonNil.map( { $0.message} ).flatMap { $0 }.reduce([String:String]()) { (accumulator, current) -> [String:String] in
guard let key = current.key, let value = current.value else { return accumulator }
var accum = accumulator
accum.updateValue(value, forKey: key)
return accum
}
The tags
dictionary now contains: ["tag1": "preview4", "tag2": "preview2"]
Elegantly populate dictionary from a struct checking nil values
It's usually not a good idea to have a dictionary with a value that is optional. Dictionaries use the assignment of nil
as an indication that you want to delete a key/value pair from the dictionary. Also, dictionary lookups return an optional value, so if your value is optional you will end up with a double optional that needs to be unwrapped twice.
You can use the fact that assigning nil
deletes a dictionary entry to build up a [String : String]
dictionary by just assigning the values. The ones that are nil
will not go into the dictionary so you won't have to remove them:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
let pairs: [(String, String?)] = [
("first", a.first),
("second", a.second),
("third", a.third)
]
var dictionary = [String : String]()
for (key, value) in pairs {
dictionary[key] = value
}
print(dictionary)
["third": "three", "first": "one"]
As @Hamish noted in the comments, you can use a DictionaryLiteral
(which internally is just an array of tuples) for pairs
which allows you to use the cleaner dictionary syntax:
let pairs: DictionaryLiteral<String,String?> = [
"first": a.first,
"second": a.second,
"third": a.third
]
All of the other code remains the same.
Note: You can just write DictionaryLiteral
and let the compiler infer the types, but I have seen Swift fail to compile or compile very slowly for large dictionary literals. That is why I have shown the use of explicit types here.
Alternatively, you can skip the Array
or DictionaryLiteral
of pairs
and just assign the values directly:
struct A {
var first: String?
var second: String?
var third: String?
}
let a = A(first: "one", second: nil, third: "three")
var dictionary = [String : String]()
dictionary["first"] = a.first
dictionary["second"] = a.second
dictionary["third"] = a.third
print(dictionary)
["third": "three", "first": "one"]
Determing dictionary with nested dictionaries type in Swift
Much much better. Never go back ;) Note that:
- First letter of structs should be capitalised. ( Linter will shoot if you don't. )
- You can sometimes embed structs, to enforce scoping. It's up to you and depends of the situation.
- You don't have to use Array<> and Dictionary<> notations. Prefer brackets.
- Define a 'string' datatype is not really advised, since it already defines a primitive type. Note that you can use `String` between quotes to redefine primitive types in your scope, but it is a bad practice.
struct Data: Hashable {
struct Text: Hashable {
let translation: String
let pinned: Bool
let order: Int
}
struct Language: Hashable {
let language: String
let target: Bool
let texts: [String: Text]
}
let base: String
let target: String
let languages: [Language]
}
If you need to use Text
type outside:
var text: Data.Text
And finally, It's a bit curious to define an array of translations in a Language
type. Maybe you should name it Translations
and structure a bit differently. for example have a Text
struct that contains a Translations
dict, with language code as the key. Just a suggestion.. Good luck :)
same value in dictionary in swift
Using the struct
approach, you could do something like this (you'll need to adapt it to your code, but that should be straightforward):
struct Categories {
let parentId: String
var values: [String] //notice this needs to be a var, not a let
}
func addItem(categories : inout [Categories], docId: String, name: String) {
if let index = categories.firstIndex(where: { $0.parentId == docId }) {
categories[index].values.append(name)
} else {
categories.append(Categories(parentId: docId, values: [name]))
}
}
func addValues() {
var categories = [Categories]()
addItem(categories: &categories, docId: "4", name: "Test1")
addItem(categories: &categories, docId: "1", name: "Test")
addItem(categories: &categories, docId: "4", name: "Test2")
addItem(categories: &categories, docId: "4", name: "Test3")
print(categories)
//in your code, it'll look more like:
// addItem(categories: &self.newCat, docId: docId, name: result?.name ?? "")
}
Which yields this:
[
StackOverflowPlayground.Categories(parentId: "4", values: ["Test1", "Test2", "Test3"]),
StackOverflowPlayground.Categories(parentId: "1", values: ["Test"])
]
I still wonder whether you maybe just want a Dictionary
that is keyed by the parentId
, but not knowing your use case, it's hard to say.
Related Topics
Swift 3 - Passing Data Between a View Controller and After That to Another 2
Animated View Floats into Place Weirdly Swiftui
Modeling Sub-Collections in Mongodb Realm Sync
Get the Exact Difference Between 2 Dates for a Single Nsdatecomponent
Clipping Sound with Opus on Android, Sent from iOS
Check the JSON Response Is Array or Int or String for a Key
Saving Array Using Nsuserdefaults Crashes App
Swift:How to Find the Position(X,Y) of a Letter in a Uilabel
Viewcontroller.Type Does Not Have a Member Names 'Answerone' - Swift
iOS Development App Startup Crash
Expand Uitextview and Uitableview When Uitextview's Text Extends Beyond 1 Line
How to Download and View Images from the New Firebase Storage
Subview Gesture Recognizer Not Being Called
Swift3: Live Uilabel Update on User Input
Error After Importing Swift into Objective-C
Allow Users to Send Messages to Multiple Users Simultaneously in a Messaging App