提问者:小点点

C#如何将数组中的所有项与搜索项列表匹配


我有一门食谱课:

public class Recipe
{
    public string Id { get; set; }
    public string RecipeTitle { get; set; }
    public string ChefName { get; set; }
    public List<string> HashTags { get; set; }
    public string Ingredients { get; set; }
}

用户可以在搜索框中输入搜索词列表,我想返回匹配所有搜索词的食谱。以下是我到目前为止的代码:

public Recipe[] SearchRecipes(string[] searchTerms, Recipe[] recipes)
{
    var matches = new List<Recipe>();

    foreach (var recipe in recipes)
    {

        if (searchTerms.Contains(recipe.ChefName, StringComparer.OrdinalIgnoreCase))
        {
            matches.Add(recipe);
        }

        foreach (var tag in recipe.Hashtags)
        {
            if (searchTerms.Contains(tag, StringComparer.OrdinalIgnoreCase))
            {
                matches.Add(recipe);
            }
        }

        foreach (var title in recipe.RecipeTitle)
        {
            if (searchTerms.Contains(title, StringComparer.OrdinalIgnoreCase))
            {
                matches.Add(recipe);
            }
        }
    }

    return matches.Distinct().ToArray();
}

但是,这将返回仅与一个或两个条件匹配的项。例如。如果用户搜索“Chef Jane”和“Donald”,它还会返回来自“Chef Callum”的东西,因为“Donald”标签存在。

如何确保返回的是完全匹配的?


共2个答案

匿名用户

我不确定在字符串中遍历成分的逻辑是什么,也许您也应该将它们列成一个列表。无论如何,您可以为每个菜谱创建一个菜谱搜索项数组,并返回所有的菜谱,其中所有用户输入的搜索项都包含这些菜谱搜索项。

public Recipe[] SearchRecipes(string[] searchTerms, Recipe[] recipes)
{
    var matches = new List<Recipe>();

    foreach (var recipe in recipes)
    {
        // Just flattening all the hash tags together with other recipe search terms
        var recipeTerms = recipe.HashTags
            .Concat(new string[]
            {
                recipe.RecipeTitle,
                recipe.ChefName,
                recipe.Ingredients
            });

        // Will include only the recipes where all search terms are matched in recipe terms
        if (searchTerms.All(searchTerm =>
                recipeTerms.Contains(searchTerm, StringComparer.OrdinalIgnoreCase))
        {
            matches.Add(recipe);
        }
    }

    return matches.ToArray();
}

您也可以手动执行all逻辑,但这种方式更简单。它来自system.linq命名空间,您可以在这里查看它的文档。

完整的LINQ解决方案如下所示:

public Recipe[] SearchRecipes(string[] searchTerms, Recipe[] recipes)
    => recipes.Where(recipe =>
        {
            var recipeTerms = recipe.HashTags
                .Concat(new string[]
                {
                    recipe.RecipeTitle,
                    recipe.ChefName,
                    recipe.Ingredients
                });

            return searchTerms.All(searchTerm =>
                recipeTerms.Contains(searchTerm, StringComparer.OrdinalIgnoreCase));
        })
        .ToArray();

匿名用户

尝试对SearchRecipes方法进行以下修改:

 public Recipe[] SearchRecipes(string[] searchTerms, Recipe[] recipes)
        {
            var matches = new List<Recipe>();

            foreach (var recipe in recipes)
            {             

                if (
                    searchTerms.Contains(recipe.ChefName, StringComparer.OrdinalIgnoreCase) &&
                    recipe.HashTags.Any(tag => searchTerms.Contains(tag, StringComparer.OrdinalIgnoreCase)) &&
                    recipe.RecipeTitle.Any(title => searchTerms.Contains(title, StringComparer.OrdinalIgnoreCase))
                   )
                    matches.Add(recipe);
            }

            return matches.Distinct().ToArray();
        }

工作原理:

  1. 遍历recipes数组,它是输入
  2. 使用逻辑&&
  3. 检查所有条件
  4. chefname是单个元素检查,对于其他元素,我们使用Linqany来遍历和匹配,如果存在单个匹配
  5. 当满足所有条件时,我们将元素添加到匹配
  6. 返回matches.distinction().toArray();在我看来不是必需的,因为我们不会多次添加相同的制作方法,您可以只返回匹配
  7. 您需要添加system.linq命名空间,此代码才能正常工作

编辑(如果Chefname可能不匹配但标题和标记匹配)

public class Match
    {
        public Recipe[] SearchRecipes(string[] searchTerms, Recipe[] recipes)
        {
            var matches = new List<Recipe>();

            var isMatch = false;

            foreach (var recipe in recipes)
            {  
                var chefNameCheck = searchTerms.Contains(recipe.ChefName, StringComparer.OrdinalIgnoreCase);
                var tagCheck = recipe.HashTags.Any(tag => searchTerms.Contains(tag, StringComparer.OrdinalIgnoreCase));
                var titleCheck =  recipe.RecipeTitle.Any(title => searchTerms.Contains(title, StringComparer.OrdinalIgnoreCase)) ;       

                if (chefNameCheck && tagCheck && titleCheck)
                    matches.Add(recipe);
                else if (tagCheck && titleCheck)
                    matches.Add(recipe);
            }

            return matches.Distinct().ToArray();
        }

    }