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

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.

Related

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)
}

Understanding equality in RealmSwift

I have some items which I fetch from Realm:
let items = realm.objects(ShoppingListItem.self)
print("\(items.count) items") // 10 items
Each item has a subdepartment, and each subdepartment has a department:
let departments = items.flatMap({ $0.product?.subdepartment?.department })
print("\(departments.count) departments") // 10 departments
My goal is to find the unique Department objects from this list. The expected result is 4. My standard approach here is to use Set:
let uniqueDepartments1 = Set(departments)
print("\(uniqueDepartments1.count) unique departments via Set") // 9 unique departments via Set - but changes every time!?
I figure there must be something I'm missing related to the way Realm treats equality. But to check, I also attempt to get the unique departments via enumeration:
var uniqueDepartments2 = [Department]()
for department in departments {
if uniqueDepartments2.contains(department) {
continue
}
uniqueDepartments2.append(department)
}
print("\(uniqueDepartments2.count) unique departments via enumeration") // 4 unique departments via enumeration
This is indeed the expected result.
Why doesn't Set work here as I expected it to? And why is the count changing each time I run it?
Edit 2/27
Here are the models in play:
class ShoppingListItem: Object {
dynamic var product: Product?
convenience init(ingredient: Ingredient) {
self.init()
self.product = ingredient.product
}
}
class Product: Object {
dynamic var productID, subdepartmentID, title: String?
dynamic var subdepartment: Subdepartment?
}
class Department: Object {
dynamic var departmentID, title: String?
}
class Subdepartment: Object {
dynamic var subdepartmentID, departmentID, title: String?
dynamic var department: Department?
}
In short: in order for an Object subclass to be correctly hashable you must currently declare a property as the primary key.
Set is built on top of a hash table. This means it checks for the existence of a specific object by first computing the hash value of the object, mapping that hash value to a bucket within the hash table, and then checking each entry in that bucket for equality with the specified object.
The nature of this implementation means that for Set to work correctly on objects of a given type, both the hashValue property and == operator must obey specific rules. In particular, any two objects for which == returns true must return the same value from their hashValue property (the reverse is not required; it's completely valid for two unequal objects to have the same hashValue). Realm's implementation's of hashValue and == don't currently meet this criteria unless your class declares a property as the primary key. When no primary key is declared, Object's default hashValue calculation defers to -[NSObject hash], which simply returns the object's address in memory. Since Object.== allows two objects with different addresses to compare equal, this violates the relationship between hashValue and == I outlined above, leading to incorrect behavior when used with Set or as the key of a Dictionary. I've filed a bug against Realm requesting that the behavior of == be fixed to be compatible with the value returned by the hashValue property.

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

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]

How to disable sorting of return array?

I have a custom rest action like this (in a class that extends yii/rest/ActiveController):
public function actionTest()
{
return ["9" => "Nine", "1" => "one"];
}
When calling the API, the array output is in reverse order, ie:
{
"1": "One"
"9": "Nine",
}
I would like to have it in the original (expected) order...
Seems like the array was sorted somewhere after the array was returned in the action, but I can't figure out where. This only happens when the array key is an integer, an array like this is sorted as expected:
["id-9" => "Nine", "id-1" => "one"]
Have tried using an ArrayDataProvider setting 'sort' = false, but that made no difference.
Since you're exporting it as json and looking at that, from this question - Keeping dictionary keys of JSON Object in order in Objective C, the answer left by Henning Makholm says that:
In JSON objects, by definition, the order of the key-value pairs is not meaningful. The specification allows a JSON producer to permute them any way it want, even at random -- and does not require a parser to preserve the ordering. RFC 4627 says:
An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array.
So json has no ordering, since it uses a dictionary as its data structure and typically dictionaries will have no implicit or explicit ordering due to the way they hash the keys for quick access.
It may be that the actual underlying representation in your program is ordered, but the json output has no such guarantee.
One way to fix this would be to move to a different data structure where order is preserved.

Comparing ObjectID's in Jade and MongoDB

Jade Template Code
each member in team.members
if member == user._id
- flag=true;
Here, team is a document in the collection 'teams' in Mongo DB, and 'members' is a property having Arrays as value.
Here values of #{member} are 53fecc27bace957811633dcc and 5412a08fa51c105017b9f1fe respectively. Both are Object Id's.
Value of #{user.id} is 53fecc27bace957811633dcc
However, this comparison returns as false and flag is never set to true.
Why is it so?
EDITED FOR CLARITY:
The mongodb-native driver uses a custom ObjectID type. Even if the ObjectIDs are the same, member and user._id would still refer to two distinct objects, and therefore the comparison fails.
Here's a simplified example of what I'm talking about. In the example below I have a constructor function for a TestObject (simulating the mongo ObjectID). I instantiate two objects (i.e. x1 and x2) both with the same parameter. While x1 and x2 are instances of the same object and both were created with the same arguments, they are actually just two different objects with the same data/payload. Therefore, the first direct comparison fails, because they in fact are not the same. To take the simulation a bit further, I added a prototype method .equals() which ensures that both objects are instances of TestObject and the payloads are identical.
function TestObject(data) {
this.payload = data;
}
TestObject.prototype.equals = function(anotherObject) {
return (anotherObject instanceof TestObject) && (this.payload === anotherObject.payload);
};
var x1 = new TestObject(1), x2 = new TestObject(1);
console.log(x1 == x2); // <-- false
console.log(x1.equals(x2)); // <-- true
You can compare them by converting the ObjectIDs to strings.
each member in team.members
if member.toString() == user._id.toString()
- flag=true;
Another possible solution would be to use the ObjectId .equal() method:
each member in team.members
if member.equals(user._id)
- flag=true;
MongoDB objectIds are probably not simple strings. As per docs, they are BSON types.
It's possible that member and user._id are two different objects that is why a "reference equals" comparison is failing, even though their string representations are the same.
On a related note, it's a bad idea to use MongoDB generated object IDs in your application. A more robust design would be to have your own identifier for every record type.