Reputation: 561
I want to know if my way of structuring data is the best method or not. The reason I have wondered about this is due to the complex requirements that I need from the data.
I am trying to check if the users list of ingredients matches the recipes list of ingredients and be able to display and count them. For example:
Ingredient count
1/2 ingredients
User has
Dough
User doesn't have
Mozzarella
The data structure for my recipe currently looks like this:
{
"RecipeData": {
"-KlKMkBekJ6plrXI5cZV": {
"name": "Pizza",
"ingredients": {
"-KkY8kL_wvxrcqMn_aP_": true,
"-KkbS1pYW_9l4y4HPuog": true
}
}
}
}
The details of the ingredients i.e. name is taken from a dictionary:
{
"IngredientData": {
"ingredients": {
"-KkbS1pYW_9l4y4HPuog": {
"name": "Dough"
},
"-KkY8kL_wvxrcqMn_aP_": {
"name": "Mozzarella"
}
}
}
}
Here is how it looks when a user adds their own ingredients:
{
"users": {
"Ss5XNpWJ5IfIuYiMqsxTnMgjNrm1": {
"usersList": {
"-KlKuq2xjbQcR3ZMsHF1": {
"name": "Dough"
}
}
}
}
}
With this data structure I have found it difficult to compare the values of the users list of ingredients with the recipe's list of ingredients.
I have a Ingredient
class that handles the properties inside users ingredients, I also use this to when getting the names of the ingredients inside the recipe:
class Ingredient {
var name: String?
var key: String?
init(from snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as? [String: Any]
self.key = snapshot.key
self.ref = snapshot.ref
self.name = snapshotValue?["name"] as? String
}
I also have an IngredientManager
which has the array of ingredients inside where I am able to call it anywhere.
I have a Recipe
class that handles the properties inside a recipe:
class Recipe {
var name: String!
var ingredients = [String]()
var key: String
init(from snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as! [String: Any]
self.name = snapshotValue["name"] as? String
self.key = snapshot.key
recipeIng(snapshot: snapshot.childSnapshot(forPath: "ingredients"))
}
// Function to count the number of ingredients inside recipe... this works
func recipeIng(snapshot: FIRDataSnapshot) {
for item in snapshot.children {
guard let ing = item as? FIRDataSnapshot else { continue }
// Gets the key and turns it into a string and then appends it to the array.
guard let value = ing.key as? String else { return }
ingredients.append(value)
}
}
// Function to match the ingredients.
func matchedIngredients() -> ([String], [String]) {
var userHas = [String]()
var userHasNot = [String]()
// loops through the users ingredients
for ingedient in IngredientManager.shared.ingredients {
// loops through the recipe's ingredients
for i in ingredients {
if ingedient.name == r {
userHas.append(i)
} else {
userHasNot.append(i)
}
}
}
return (userHas, userHasNot)
}
}
I am able to count the number of ingredients inside a recipe but right now I am struggling to compare that with the users list of ingredients. ingredient.name
is able to get the name of the users ingredients which is what I'd like to use to compare it with as the key for the users ingredients are completely different to the recipes.
This is how I display it on a label:
ingredientLbl.text = "\(recipe.matchedIngredients().0.count)/\(recipe.ingredients.count)"
Upvotes: 0
Views: 432
Reputation: 598708
Due to the extra lookup, it becomes a bit of a nesting exercise. But if you read through the loops it's actually not all that difficult. In JavaScript this does the trick:
ref.child("users").once("value").then((usersSnapshot) => {
usersSnapshot.forEach((userSnapshot) => {
ref.child("RecipeData").once("value").then((recipesSnapshot) => {
recipesSnapshot.forEach((recipeSnapshot) => {
var userNeeds = [];
recipeSnapshot.child("ingredients").forEach((recipeIngredientSnapshot) => {
userNeeds.push(ingredientsData[recipeIngredientSnapshot.key].name); // assume the user needs this
var recipeIngredientName = ingredientsData[recipeIngredientSnapshot.key];
userSnapshot.child("usersList").forEach((userIngredientSnapshot) => {
var index = userNeeds.indexOf(userIngredientSnapshot.val().name)
if (index >= 0) {
userNeeds.splice(index, 1);
}
});
console.log("User "+userSnapshot.key+" "+(userNeeds.length?"cannot":"can")+" make "+recipeSnapshot.key+": missing ingredients "+userNeeds);
});
});
});
});
});
A working jsbin: https://jsbin.com/ziqobow/edit?js,console
The key that I do differently here is that I first assume that the user doesn't have each necessary ingredient, and then remove that assumption if it turns out they do already have the ingredient.
Upvotes: 0