So, a little background info:
I use a Dictionary to store info from wich a collectionview is populated and the info in the dictionary is paired with data from a urlsession.
My Dictionary looks like this:
var allDict = ["Section1": ["device1", "device2"], "Section2": ["device3", "device4"]]
I then use a struct to get an array which feeds my collectionview cells and sections:
struct Objects {
var sectionName : String!
var sectionObjects : [String]!
}
var objectArray = [Objects]()
for (key, value) in allDict {
objectArray.append(Objects(sectionName: key, sectionObjects: value))
}
The outcome is this:
ObjectArray: [__lldb_expr_134.Objects(sectionName: Section1, sectionObjects: ["device1", "device2"]), __lldb_expr_134.Objects(sectionName: Section2, sectionObjects: ["device3", "device4"])]
["device1", "device2"]
Now, the Problem is that I want to be able to rearrange the cells and I am able to rearrange the data in the array, but I have to get it back to the dictionary.
I'm aware that a dicitonary is unsorted, but while testing I never hat the problem that the devices changed there place in the array inside the dicitonary.
My idea is to rearrange the ObjectArray and then clear the dictionary and put the data back in.
So I tried to get the data from ObjectArray back using a for loop but I dont get it right, here is what I tried:
I found out I can access the data like this:
print(objectArray[0].sectionObjects)
["device1", "device2"]
So I tried:
for items in objectArray {
print(items)
for (name) in items.sectionObjects {
print(name)
}
}
there I get this:
device1
device2
But when I do:
for items in objectArray {
print(items)
for (name) in items.sectionName {
print(name)
}
}
I get this:
S
e
c
t
i
o
n
1
All I want is something like:
for (sectionNAme, sectionObjects) in objectArray {
newDictionary[sectionName] += [sectionObjects]
}
but this gets me this: Expression type '[Objects]' is ambiguous without more context
Sorry, for the long text...
You have to write the loop in this way
for object in objectArray {
newDictionary[object.sectionName] = object.sectionObjects
}
object represents one Objects instance – by the way it's recommended to name the struct in singular form Object – you get the name with object.sectionName and the objects with object.sectionObjects
Notes:
S
e
c
t
i
o
n
1
occurs because you treat the name string as array and you are iterating through the characters of the string.
Since you are using the implicit memberwise initializer anyway I would declare the struct with non-optional members as Section and shorten the member names:
struct Section {
var name : String
var objects : [String]
}
Related
I need to query MongoDB Realm from synced iOS and Android app. In Swift I can write something like this:
let dictionary = realm?.objects(myReamlObject.self)
let results = dictionary?.where {
$0.senses.glosses.term == "the term I want"
}
or using predicate:
let results = dictionary?.filter("ANY senses.glosses.term == %#", "the term I want")
Both work well, but I don't want to check ALL senses.glosses.term.
Every entry has (or could have) many senses and many glosses.
I would like to check term of first senses in first glosses only.
Something I would write like this:
let results = dictionary?.where {
$0.senses[0].glosses[0].term == "the term I want"
}
But it gives error:
Referencing subscript 'subscript(_:)' on 'Query' requires that
'List<myRealmObject_senses>' conform to 'RealmKeyedCollection'
Any suggestion on how to query only first index of an array in MongoDB Realm? Thank you
Let me re-state the question
How to query a Realm objects' List property - but only query on the first
element in the List.
The answer is going to depend on the amount of results being worked with.
Here's how to do it in a way that's O.K. for small datasets but NOT RECOMMENDED
Your models were not included in the question so let me use a simplified model of a PersonClass that as a List of DogClass objects
class PersonClass: Object {
#Persisted var name = ""
#Persisted var dogList = List<DogClass>()
}
class DogClass: Object {
#Persisted var name = ""
}
The idea here is to use Swift high-level functions to only test the first item in each persons doglist for a match (this can be applied to other languages as well)
//get all the people
let peopleResults = realm.objects(PersonClass.self)
//use the high-level Swift function compactMap to return all of
// the people whose first dog is named "Spot"
let persons = peopleResults.compactMap { person -> PersonClass? in
if person.dogList.first?.name == "Spot" {
return person
}
return nil
}
The downside is that this code overrides a fundamental advantage of Realm - that Realm objects are lazily loaded if Realm functions are used e.g. as soon as a high-level Swift function is used, ALL of the objects are loaded into memory, potentially overwhelming the device.
A better option is to simply add a managed property to the PersonClass that also points to the 0'th element in the list.
class PersonClass: Object {
#Persisted var name = ""
#Persisted var dogList = List<DogClass>()
#Persisted var mainDog: DogClass?
func addMainDog(withDog: DogClass) {
self.dogList.append(withDog)
self.mainDog = withDog
}
}
as you can see, there's also a function to add that first dog to the list and also populates the mainDog property which points to the same object. It's one property so the overall impact is very low but the advantages for simple queries are very high.
From there the query becomes trivial
let peopleResults = realm.objects(PersonClass.self).where { $0.mainDog.name == "Spot" }
Expanding on this, you could save the 0th element of each List object in the Parent object or even have a property in each child object that points to the first element in it's respective list.
I have an array of enums and I'd like to remove x number of elements from it, the code below is what I'm trying to achieve, it partially works because it removes elements only from the more variable created in the switch-case but the original array doesn't change
MyArray of enums
Contacts
More
if more is present it means there are more contacts to download, when the user tap on a button it should remove ids that has been downloaded
Here is an example:
// Switch last element of my array
switch model.myArray[model.myArray.count-1] {
// If last element is More type
case var more as More:
// Download new contacts
downloadfunction()
// Remove 100 contacts that has been downloaded
let range = 0...99
more.peopleIds?.removeSubrange(range)
}
More structure
public struct More: Decodable, Identifiable {
public let id: String?
public var peopleIds: [String]?
I think the best way to check the type of the last element of the array is to cast it using an if var ...
if var more = model.myArray.last as? More {
and then change it and replace the old value in the array
if var more = myArray.last as? More, let array = more.peopleIds {
more.peopleIds = Array(array.dropFirst(100))
myArray[myArray.endIndex - 1] = more
}
I want to initialize every time a struct with dictionaries. Later, I'm going to use its properties instead a dictionary's keys and values - it seems rather easier. However, when I try the code below, it tells me that "Return from initializer without initializing all stored properties" and "1. 'self.one' not initialized" and "2. 'self.two' not initialized". My question is how to initialize a struct from a dictionary, so that I have basically a struct with the contents of the dictionary? Or how to transform it into struct?
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
one = ""
two = []
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
for in clause may have zero runs, in which case struct properties will not be initialized. You have to provide default values (or emit fatalError if you really need to).
While I think your example is pure synthetical, there is no need to loop through array, you can set properties to its last entry.
The issues is that if three is an empty Dictionary, the instance properties one and two don't get initialised. Also, you are overwriting the properties in each iteration of the for loop and the compiler cannot guarantee that there will be any iterations of the loop in compile-time, hence the compiler error.
You could make the initialiser failable to account for this by checking that the dictionary actually contains at least one key-value pair and assigning that first key-value pair to your properties.
struct Blabla {
var one: String
var two: [Int]
init?(three: [String: [Int]]) {
guard let key = three.keys.first, let value = three[key] else { return nil }
one = key
two = value
}
}
However, you should rethink what it is that you are actually trying to achieve, since with your current setup you have a mismatch between your init input values and the properties of your struct.
This code should compile, but it feels unsafe to me to initialize a Struct in this way because:
It assume your dictionary has values in it.
Your stored properties will always have the last value you looped through.
In order to pull values out to satisfy the compiler you need to force unwrap them. (With Dávid Pásztor's guard-letting approach, this can be avoided)
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
self.one = three.keys.first!
self.two = three[three.keys.first!]!
}
}
let input = ["pizza": [1,2]]
let l = Blabla(three: input)
If I were you I would let the memberwise initializer that you get for free do its thing and provide either a specialized initializer to handle your case of taking a Dictionary as input or move that parsing to another function/class/etc....
The compiler error is clear: If the dictionary is empty the struct members are never initialized. But the code makes no sense anyway as each iteration of the dictionary overwrites the values.
Maybe you mean to map the dictionary to an array of the struct
struct Blabla {
let one: String
let two: [Int]
}
let three = ["A":[1,2], "B":[3,4]]
let blabla = three.map{Blabla(one: $0.key, two: $0.value)}
print(blabla) // [Blabla(one: "A", two: [1, 2]), Blabla(one: "B", two: [3, 4])]
struct blabla{
var a : string
var b : [int] = []
init(_ data: [string:[int]]){
// whatever you want to do
}
}
I have a variable set of objects that I need to place in a dictionary. I'm trying to add them to the dictionary in a for loop but from what I'm understanding dictionaries are immutable so they need to be declared immediately. How do I create a dictionary list of items that are not predetermined?
var newItems = [:]
for item in self.items{
newItems["\(item.key)"]["name"] = "A new item"
}
does not use the second value
var newItems : [String:String] = [:]
for i in 1..10{
newItems[i.description] = "A new item"
}
for more information https://www.weheartswift.com/dictionaries/
The problem with your original code is that dictionaries only have one key, so this construct newItems["\(item.key)"]["name"] is syntactically incorrect. If you had a fixed number of properties you could use a struct and put that in a dictionary. As you posed the question, though, you need to create a dictionary where the stored elements themselves are dictionaries. So, although I didn't actually put this into Xcode, it's a template for what you need to do:
var newItems = [:[:]]()
for item in self.items {
var itemDict = [:]()
for prop in whereeveryourpropertiescomefrom {
itemDict[prop] = valueforthisproperty
}
newItems["\(item.key)"] = itemDict
}
Of course, if your properties were initially stored in a dictionary unique to this item (equivalent of the inner loop), just store it directly into newItems.
Then, you could reference things as
let value = newItems["\(item.key)"]?.["property key"]
Notice the dictionary retrieval returns an optional you have to deal with.
The solution was when initializing the dictionary to create another dictionary
var newItems: [String:[String:AnyObject]]()
I have a JSON that might contain an array of string elements and I want to save it to a variable. So far I did:
import SwiftyJSON
(...)
var myUsers = [""]
if(json["arrayOfUsers"].string != nil)
{
myUsers = json["arrayOfUsers"] //this brings an error
}
The error says:
cannot subscript a value of type JSON with an index of type string
How can I pass this array safely to my variable?
You have to get the array of Strings that SwiftyJSON has prepared when it parsed your JSON data.
I will use if let rather than != nil like you do in your question, and we're going to use SwiftyJSON's .array optional getter:
if let users = json["arrayOfUsers"].array {
myUsers = users
}
If for any reason you get a type error, you can explicitly downcast the SwiftyJSON object itself instead of using the getter:
if let users = json["arrayOfUsers"] as? [String] {
myUsers = users
}
Note that your array of Strings is also not created properly. Do like this:
var myUsers = [String]()
or like hits:
var myUsers: [String] = []
Both versions are equally valid and both create an empty array of strings.