How to access a Data Model [String: [String]] in UITableView with sections? - swift

I'm trying to prep my Data Model so it can be used in a UITableView with sections.
var folderHolder: [String: [String]]?
folderHolder = ["Projects": ["All", "Recent"], "Smart Folders": ["Folder 1", "Folder 2", "Folder 3"]]
How can I access the keys and objects in this dictionary via an index (as needed by the UITableView)
I tried this in the playground and got stuck. Thank you for your help with this.
// Need number of Keys
// Expected result: 2
folderHolder!.count
// Need number of elements in Key
// Expected: All and Recent are in Projects, so 2 would be expected
folderHolder!["Projects"]
folderHolder!["Projects"]!.count
// How can I get this result by stating the index, e.g. writing 1 as a parameter instead of "Smart Folders"
folderHolder![1]!.count
// Need specific element
// Input parameter: Key index, Value index
// Expected: "Folder 2"
folderHolder![1]![1]
// I don't know why it only works when I state the key explicitly.
folderHolder!["Smart Folders"]![1]
Screenshot with Playground results

The way that dictionaries are set up, you cannot index them in the same way that you would index an array. Due to the Key: Value nature of dictionaries, the order is not important, and thus subscripting like so: folderHolder[1] will not work. Indexing like that would only work in an array, where the order is important and thus maintained.
The Swift Documentation here states that:
A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering. Each value is associated with a unique key, which acts as an identifier for that value within the dictionary. Unlike items in an array, items in a dictionary do not have a specified order.

Found out the solution after a bit more research:
The Dictionary keys need to be converted into an array. The array items can be accessed via an index (the section of the UITableView) and return the name of the Key. And the name of the key can be used to access the Value of the Dictionary (the row of the UITableView).
Here the correct playground data as a reference:
var folderHolder: [String: [String]]?
folderHolder = ["Projects": ["All", "Recent"], "Smart Folders": ["Folder 1", "Folder 2", "Folder 3"]]
let folderHolderArray = Array(folderHolder!.keys)
// Need number of Keys
// Expected: 2
folderHolder!.count
folderHolderArray.count
// Need number of elements in Key
// Expected: All and Recent are in Projects, so 2 would be expected
folderHolder!["Projects"]
folderHolder!["Projects"]!.count
// How can I get this result by stating the index, e.g. writing 1 as a parameter instead of "Smart Folders"
folderHolderArray[1]
// Need specific element
// Input parameter: Key index, Value index
// Expected: "Folder 2"
//folderHolder![1]![1]
let folderHolderSection = folderHolderArray[1]
let folders = folderHolder![folderHolderSection]
let folder = folderHolder![folderHolderSection]![1]

Related

Why Swift Set fucntion Why firstIndex(of: ) Apply?

I understand Set Collection is key-value and Keys are not duplicated.
In the example below, I thought fruits were the key.
however .firstIndex(of: ) is exist why?
So can a second index exist?
Am I misunderstanding the set?
var favoriteFruits: Set = ["Banana", "Apple", "Orange", "Orange"]
favoriteFruits.insert("WaterMelon")
print(favoriteFruits)
favoriteFruits.remove("Banana")
print(favoriteFruits)
if favoriteFruits.contains("Tomato") {
print("Tomato is my favorite Fruits")
} else {
print("Tomato is not my favorite Fruits")
}
print(favoriteFruits.firstIndex(of: "Orange"))
It would be of great help if you leave a comment.
If you check firstIndexOf method's explanition you will see a defition :
Available when Element conforms to Equatable.
Sets conforms to Equatable.You can also test this while checking the equality of the two sets.Below code doesn't give any error.
if set1 == set2{
//....
}
You ask .firstIndex(of: ) is exist why?
So there is no obstacle for the collection type of Set to use the firstIndexOf method
If you ask why set is equatable.Set uses the hash table to check the elements inside. So the Set must be hashable.An object that conforms to Hashable must also be Equatable too
This is a consequence of Set<String> conforming to Collection<String>. The Collection protocol requires this method:
func firstIndex(of element: Self.Element) -> Self.Index?
The associated types Element and Index are also required by the Collection protocol. Even if Set is not index-based, Set does declare a nested struct called Index. See its implementation here.
It is true that there can be no "second" index for a particular element, since elements in a set are unique. firstIndex(of:) and lastIndex(of:) for any element for a set would return the same thing.
However, the Collection protocol does not care. After all, functions that can take any Collection does not know what kind of collection they are working with, e.g.
func someGenericFunction<C: Collection>(collection: C) {
// ...
}
so they need to specify whether they want the first, last, or the nth index of a particular element, so that their code works for all kinds of collections. If Set did not have these methods, then Set is not a Collection, and cannot be passed to these functions.

Is it possible to change the field name in user storage with a string?

If you write conv.user.storage.something = "test" I understand that, but can you change the field name "something" in this example. Say you had an array of values named Fruit ["Banana", "Apple"] can you read the array and then assign those values to the field name value. It seems like you can only set the field name manually such as conv.user.storage.Banana but what if I am getting those names from an array?
Yes you can iterate through the array and directly assign the keys.
for (const element of array) {
conv.user.storage[element] = "text"
}

Realm Swift - How to remove an item at a specific index position?

I am storing a simple list of id's as GUIDs in Realm, but would like the ability to delete an object at a particular index position.
So for example, I want to remove 04b8d81b9e614f1ebb6de41cb0e64432 at index position 1, how can this be achieved? Do I need to add a primary key, or is there a way to remove the item directly using the given index position?
Results<RecipeIds> <0x7fa844451800> (
[0] RecipeIds {
id = a1e28a5eef144922880945b5fcca6399;
},
[1] RecipeIds {
id = 04b8d81b9e614f1ebb6de41cb0e64432;
},
[2] RecipeIds {
id = cd0eead0dcc6403493c4f110667c34ad;
}
)
It seems like this should be a straightforward ask, but I can't find any documentation on it. Even a pointer in the right direction would do.
Results are auto-updating and you cannot directly modify them. You need to update/add/delete objects in your Realm to effect the state of your Results instance.
So you can simply grab the element you need from your Results instance, delete it from Realm and it will be removed from the Results as well.
Assuming the Results instance shown in your question is stored in a variable called recipes, you can do something like the following:
let recipeToDelete = recipes.filter("id == %#","04b8d81b9e614f1ebb6de41cb0e64432")
try! realm.write {
realm.delete(recipeToDelete)
}

Search Array with depending order

I got a question regarding filtering an array.
Let's assume I got an array of country names:
let countries = [Albania, Bahrain, Barbados, Denmark, France, Zimbabwe]
now I want to filter this array to check if it contains a certain String let say "ba".
I can do this easily with
countries = countries.filter{ $0.contains("ba")}
which returns
Albania, Bahrain, Barbados, Zimbabwe
But I actually want the order of letters to matter. Therefore, the result "Albania" and "Zimbabwe" should not appear and only "Bahrain" and "Barbados" as their name starts with an Ba.
Is there any way to do this to avoid a huge for loop going through all entites checking individually for each character?
Use .hasPrefix instead of .contains, like this:
print(countries.filter{ $0.hasPrefix("Ba") })
Note that this is case sensitive. BTW, in your example, the problem was not missing order of letters but the fact that .contains respects case as do most methods in swift.
You can take care of the case and the filtering in one step:
let countries = ["Albania", "Bahrain", "Barbados", "Denmark", "France", "Zimbabwe"]
let filtered = countries.filter { $0.lowercased().hasPrefix("ba") } // -> ["Bahrain", "Barbados"]
This lowercases the country names before applying the filter's test, but doesn't change the original array, so the results have the same case.
This is important because you might incorrectly want to do this in two steps for readability:
countries
.map { $0.lowercased()}
.filter { $0.hasPrefix("ba")}
But this returns ["bahrain", "barbados"] because the filter is being applied to the now lowercased array.

Composite views in couchbase

I'm new to Couchbase and am struggling to get a composite index to do what I want it to. The use-case is this:
I have a set of "Enumerations" being stored as documents
Each has a "last_updated" field which -- as you may have guessed -- stores the last time that the field was updated
I want to be able to show only those enumerations which have been updated since some given date but still sort the list by the name of the enumeration
I've created a Couchbase View like this:
function (doc, meta) {
var time_array;
if (doc.doc_type === "enum") {
if (doc.last_updated) {
time_array = doc.last_updated.split(/[- :]/);
} else {
time_array = [0,0,0,0,0,0];
}
for(var i=0; i<time_array.length; i++) { time_array[i] = parseInt(time_array[i], 10); }
time_array.unshift(meta.id);
emit(time_array, null);
}
}
I have one record that doesn't have the last_updated field set and therefore has it's time fields are all set to zero. I thought as a first test I could filter out that result and I put in the following:
startkey = ["a",2012,0,0,0,0,0]
endkey = ["Z",2014,0,0,0,0,0]
While the list is sorted by the 'id' it isn't filtering anything! Can anyone tell me what I'm doing wrong? Is there a better composite view to achieve these results?
In couchbase when you query view by startkey - endkey you're unable to filter results by 2 or more properties. Couchbase has only one index, so it will filter your results only by first param. So your query will be identical to query with:
startkey = ["a"]
endkey = ["Z"]
Here is a link to complete answer by Filipe Manana why it can't be filtered by those dates.
Here is a quote from it:
For composite keys (arrays), elements are compared from left to right and comparison finishes as soon as a element is different from the corresponding element in the other key (same as what happens when comparing strings à la memcmp() or strcmp()).
So if you want to have a view that filters by date, date array should go first in composite key.