Структурируйте данные для сравнения значений с помощью Firebase и Swift 3.

Я хочу знать, является ли мой способ структурирования данных лучшим или нет. Причина, по которой я задавался этим вопросом, связана со сложными требованиями, которые мне нужны от данных.

Я пытаюсь проверить, соответствует ли список ингредиентов users списку ингредиентов recipes, и иметь возможность отображать и подсчитывать их. Например:

Количество ингредиентов

1/2 ingredients

Пользователь

Dough

У пользователя нет

Mozzarella

Структура данных для моего рецепта в настоящее время выглядит так:

{
  "RecipeData": {
    "-KlKMkBekJ6plrXI5cZV": {
      "name": "Pizza",
      "ingredients": {
        "-KkY8kL_wvxrcqMn_aP_": true,
        "-KkbS1pYW_9l4y4HPuog": true
      }
    }
  }
}

Подробная информация об ингредиентах, т.е. название, взята из словаря:

{
  "IngredientData": {
    "ingredients": {
      "-KkbS1pYW_9l4y4HPuog": {
        "name": "Dough"
      },
      "-KkY8kL_wvxrcqMn_aP_": {
        "name": "Mozzarella"
      }
    }
  }
}

Вот как это выглядит, когда пользователь добавляет свои ингредиенты:

{
  "users": {
    "Ss5XNpWJ5IfIuYiMqsxTnMgjNrm1": {
      "usersList": {
        "-KlKuq2xjbQcR3ZMsHF1": {
          "name": "Dough"
        }
      }
    }
  }
}

С этой структурой данных мне было трудно сравнивать значения списка ингредиентов пользователей со списком ингредиентов рецепта.

Обновление:

У меня есть класс Ingredient, который обрабатывает свойства внутри пользовательских ингредиентов, я также использую его при получении названий ингредиентов внутри рецепта:

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
    }

У меня также есть IngredientManager, в котором есть набор ингредиентов, и я могу вызвать его куда угодно.

У меня есть класс 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)
    }
}

Я могу подсчитать количество ингредиентов в рецепте, но сейчас я изо всех сил пытаюсь сравнить это со списком ингредиентов users. ingredient.name может получить название ингредиентов users, которые я хотел бы использовать для сравнения, поскольку ключ для ингредиентов users полностью отличается от рецепты.

Вот как я отображаю это на этикетке:

ingredientLbl.text = "\(recipe.matchedIngredients().0.count)/\(recipe.ingredients.count)"

person Chace    schedule 16.06.2017    source источник
comment
Это выглядит хорошо для меня. Вероятно, вам будет легче помочь, если вы покажете, что вы пробовали. Я ожидаю цикл по одному списку, где вы удаляете элемент из обоих списков, если он существует в обоих. В конце цикла оба списка должны быть пустыми.   -  person Frank van Puffelen    schedule 16.06.2017
comment
@FrankvanPuffelen, пожалуйста, посмотрите мой обновленный ответ, если вы хотите, чтобы я показал вам, как я могу получить названия каждого ингредиента в рецепте, который я могу, я просто не хотел выглядеть так, как будто я просто публикую много кодов.   -  person Chace    schedule 16.06.2017
comment
Я думаю, что суть вашей проблемы заключается в вашем последнем предложении: ключевые ингредиенты для пользователей полностью отличаются от рецептов. Чтобы можно было сравнивать ингредиенты, вы действительно должны использовать для них один и тот же ключ. Он может также работать на основе сравнения имен, но код будет труднее читать/сопровождать.   -  person Frank van Puffelen    schedule 16.06.2017
comment
У меня было ощущение, что дело в этом. Мне было интересно, не могли бы вы помочь мне разрешить пользователю добавлять ингредиенты, которые имеют те же ключи, что и ингредиенты для рецепта. На данный момент мне пришлось вручную добавлять ингредиенты в рецепт, например `-KkbS1pYW_9l4y4HPuog: true`. Я не знаю, как добавить ингредиент, взятый из словаря ингредиентов, в список пользователей. Если этот вопрос не имеет отношения к первоначальному вопросу для этого поста, я задам его где-нибудь еще.   -  person Chace    schedule 16.06.2017


Ответы (1)


Из-за дополнительного поиска это становится чем-то вроде упражнения по вложению. Но если вы читаете циклы, на самом деле это не так уж и сложно. В JavaScript это помогает:

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

Рабочий jsbin: https://jsbin.com/ziqobow/edit?js,console

Ключевой момент, который я здесь делаю по-другому, заключается в том, что я сначала предполагаю, что у пользователя нет всех необходимых ингредиентов, а затем удаляю это предположение, если выясняется, что они есть у него уже есть.

person Frank van Puffelen    schedule 16.06.2017
comment
Извините, но не могли бы вы написать это на Swift, как задает вопрос, пожалуйста. - person Chace; 16.06.2017