0

I have to convert 2 functions, both using filter methods into something using for loops, how am i supposed to do that ? For some case, it makes sense, but using the push method is a bit confusing.

Thanks in advance

First function :

function filterWithInputValue(recipes) {
    filteredRecipes = recipes.filter(recipe => {
        const lowerCaseName = recipe.name.toLowerCase().includes(input.value.toLowerCase());
        const lowerCaseDescription = recipe.description.toLowerCase().includes(input.value.toLowerCase());
        const lowerCaseIngredients = recipe.ingredients.filter(({ ingredient }) => ingredient.toLowerCase().includes(input.value.toLowerCase())).length > 0
        return  lowerCaseIngredients | lowerCaseName | lowerCaseDescription;
    });
};

My current version of the function :

function filterWithInputValue(recipes) {
    for (let recipe of recipes) {
        const lowerCaseName = recipe.name.toLowerCase().includes(input.value.toLowerCase());
        const lowerCaseDescription = recipe.description.toLowerCase().includes(input.value.toLowerCase());
        
        for (let ingredient of recipe.ingredients) {
            //????
        }
        
        if (lowerCaseName | lowerCaseDescription | lowerCaseIngredient) {
            filteredRecipes.push(recipe)
        };
    };
};

Second function :

                if(tags.length >= 1) {
                    if(input.value.length >= 3) filterWithInputValue(recipes)
                    else filteredRecipes = recipes

                    const tagList = document.querySelectorAll(".tag");
                    
                    tagList.forEach((tag) => {
                        if(tag.classList.contains("tag0")) {
                            filteredRecipes = filteredRecipes.filter(recipe => recipe.ingredients.some(
                                ({ingredient}) => ingredient.includes(tag.innerText)))
                        }
                        if(tag.classList.contains("tag1")) filteredRecipes = filteredRecipes.filter(recipe => recipe.appliance.includes(tag.innerText))
                        if(tag.classList.contains("tag2")) filteredRecipes = filteredRecipes.filter(recipe => recipe.ustensils.includes(tag.innerText))
                    })
                    createRecipeCard(filteredRecipes)
                    mainInputFiltering(filteredRecipes)
                };

Example of a recipes/data :

{
        "id": 1,
        "name" : "Limonade de Coco",
        "servings" : 1,
        "ingredients": [
            {
                "ingredient" : "Lait de coco",
                "quantity" : 400,
                "unit" : "ml"
            },
            {
                "ingredient" : "Jus de citron",
                "quantity" : 2
            },
            {
                "ingredient" : "Crème de coco",
                "quantity" : 2,
                "unit" : "cuillères à soupe"
            },
            {
                "ingredient" : "Sucre",
                "quantity" : 30,
                "unit" : "grammes"
            },
            {
                "ingredient": "Glaçons"
            }
        ],
        "time": 10,
        "description": "Mettre les glaçons à votre goût dans le blender, ajouter le lait, la crème de coco, le jus de 2 citrons et le sucre. Mixer jusqu'à avoir la consistence désirée",
        "appliance": "Blender",
        "ustensils": ["cuillère à Soupe", "verres", "presse citron" ]
    }
2
  • I'm curious as to why you can't use filter. Would it be acceptable to create a generic filter method (that doesn't use Array.prototype.filter) and use that? Commented May 7, 2022 at 8:02
  • I have to provide two version, one using filter, map and other methods, one using for/while loops and compare speeds. It's also very curious for me.. Commented May 7, 2022 at 8:10

1 Answer 1

1

For a fairly straight forward conversion your first function could look like the following (not tested):

function filterWithInputValue(recipes) {
  let filteredRecipes = []; // create array to hold the results of filtering the recipes

  for (let recipe of recipes) {
    const lowerCaseName = recipe.name.toLowerCase().includes(input.value.toLowerCase());
    const lowerCaseDescription = recipe.description.toLowerCase().includes(input.value.toLowerCase());

    let filteredIngredients = []; // create array to hold the results of filtering the ingredients
    for (let { ingredient } of recipe.ingredients) {
      if(ingredient.toLowerCase().includes(input.value.toLowerCase())) {
        filteredIngredients.push(ingredient); // push those that succeed on the condition into the results array
      }
    }
    
    let lowerCaseIngredient = filteredIngredients.length > 0;

    if (lowerCaseName | lowerCaseDescription | lowerCaseIngredient) {
      filteredRecipes.push(recipe); // push those that succeed on the condition into the results array
    }
  }

  return filteredRecipes; // return the results of filtering
}

the general priciple here is that you can create an array to accumulate results, add things that succeed at the conditional (that you take from your filter method) to this array and then return the array.

A slightly cleaner version of the above could look like:

function filterIngredients(ingredients, lowercaseInput) {
  let filteredIngredients = []; // create array to hold the results of filtering the ingredients
  for (let { ingredient } of ingredients) {
    if(ingredient.toLowerCase().includes(lowercaseInput)) {
      filteredIngredients.push(ingredient); // push those that succeed on the condition into the results array
    }
  }
  return filteredIngredients;
}

function filterWithInputValue(recipes) {
  let lowercaseInput = input.value.toLowerCase();
  let filteredRecipes = []; // create array to hold the results of filtering the recipes

  for (let recipe of recipes) {
    const lowerCaseName = recipe.name.toLowerCase().includes(lowercaseInput);
    const lowerCaseDescription = recipe.description.toLowerCase().includes(lowercaseInput);

    const lowerCaseIngredient = filteredIngredients(recipe.ingredients, lowercaseInput).length > 0;

    if (lowerCaseName | lowerCaseDescription | lowerCaseIngredient) {
      filteredRecipes.push(recipe); // push those that succeed on the condition into the results array
    }
  }

  return filteredRecipes; // return the results of filtering
}

where you can probably see that the filtering of the ingredients is following the same general pattern as the filtering of the recipes.

A couple of notes:

  1. It looks like the filter on the ingredients could actually be a some instead as it looks like ingedients are just being checked for any matches and doesn't care about the number or details of them.
  2. The original filterWithInputValue does not actually return anything so I'm wondering how it gets used / checked.
Sign up to request clarification or add additional context in comments.

1 Comment

It works that way. The function filterWithInput is part of a bigger function containing an eventListener("input") who will trigger a new search each time the user is changing the value. Now i will keep working on the second function. Thanks for your help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.