我无法让这个LINQ返回任何结果,我有几个这样的类:
public class RestaurantMenu
{
public List<MenuItem> MenuItems { get; set; }
}
public class MenuItem
{
public decimal Price { get; set; }
public List<FoodItem> FoodItems { get; set; }
}
public class FoodItem
{
public string Label { get; set; }
}
给定一个RestaurantMenu
列表,我试图返回任何匹配的结果,其中餐厅菜单的菜单项的食品标签与列表中的所有字符串匹配。基本上,你输入你想要吃的食物,它应该返回任何你想要吃的东西的餐馆。它应该支持多个字符串,但我甚至无法匹配一个字符串。
假设以下内容:
List<RestaurantMenu> allRestaurantMenus = /blah;
List<string> labelOfFoodsDesired = /blah;
我尝试通过链接像这样的表达式来实现它:
IQueryable<RestaurantMenu> query = allRestaurantMenus.AsQueryable();
foreach (string foodItem in labelOfFoodItemsDesired)
{
query = query.Where(x => x.MenuItems.Any(y => y.FoodItems.Select(z => z.Label).Contains(foodItem)));
}
List<RestaurantMenu> matchingRestaurantMenus = query.ToList();
但是它总是没有返回任何结果,即使通过调试我也确定有匹配。我该如何撰写此查询?
答案 0 :(得分:3)
allRestaurantMenus.Where(m =>
m.MenuItems.Any(mi =>
!labelOfFoodsDesired.Except(mi.FoodItems.Select(fi => fi.Label)).Any()))
工作原理:我们正在过滤菜单,其中至少有一个菜单项,其中包含您传递的所有标签。使用Enumerable.Except
!labelOfFoodsDesired.Except(mi.FoodItems.Select(fi => fi.Label)).Any()
我们在所需标签和食品的所有标签之间产生设定差异。如果食品标签中存在所有需要的标签,则设置为空。
更新:如果您应该检查任何菜单项(而不是单个菜单项),那么查询将类似于
allRestaurantMenus.Where(m =>
!labelOfFoodsDesired.Except(
m.MenuItems.SelectMany(mi => mi.FoodItems.Select(fi => fi.Label))).Any())
工作原理:方法与上述相同,但您应检查所有菜单项的所有标签。这是通过Enumerable.SelectMany完成的,它将菜单项集合展平为所有标签的集合。然后,如上所述,您可以在所需标签和所有菜单项的所有标签之间建立设置差异。如果设置为空,则菜单满足您的条件。
TEST:给出以下菜单
List<RestaurantMenu> allRestaurantMenus = new List<RestaurantMenu> {
new RestaurantMenu {
MenuItems = new List<MenuItem> {
new MenuItem {
FoodItems = new List<FoodItem> {
new FoodItem { Label = "Chocolate" },
new FoodItem { Label = "Water" }
}
},
new MenuItem {
FoodItems = new List<FoodItem> {
new FoodItem { Label = "Egg" },
new FoodItem { Label = "Ketchup" }
}
}
}
},
new RestaurantMenu {
MenuItems = new List<MenuItem> {
new MenuItem {
FoodItems = new List<FoodItem> {
new FoodItem { Label = "Water" }
}
},
new MenuItem {
FoodItems = new List<FoodItem> {
new FoodItem { Label = "Banana" },
new FoodItem { Label = "Peach" }
}
}
}
}
};
并遵循所需的标签
List<string> labelOfFoodsDesired = new List<string>
{
"Water", "Banana"
};
上面的查询会将菜单展平为所有标签的序列:
{ "Chocolate", "Water", "Egg", "Ketchup" }
{ "Water", "Banana", "Peach" }
然后它将在所需标签和这种扁平化结果之间建立差异:
{ "Banana" }
{ }
因此第二个结果为空(菜单中存在所有需要的标签),只有第二个菜单匹配。
答案 1 :(得分:0)
我觉得这样的事情会起作用。我确定您可以删除foreach
,以便它只是一个linq查询。
foreach (var restaurant in allRestaurantMenus)
{
var restaurantFoodItems = restaurant.MenuItems.SelectMany(m => m.FoodItems);
bool hasAllFoods = labelOfFoodsDesired.All(lf => restaurantFoodItems.Contains(lf));
if (hasAllFoods)
yield return restaurant;
}
答案 2 :(得分:0)
您可以使用Where
和Except
- Any
的这种组合:
var allMatches = allRestaurantMenus
.Where(rm => !labelOfFoodsDesired
.Except(rm.MenuItems.SelectMany(f => f.FoodItems.Select(fi => fi.Label)))
.Any());
如果一个或多个所需标签不可用,则 labelOfFoodsDesired.Except(allmenu-food-item-labels).Any()
会返回true
。 !
使条件反转,以便检查所有食物的可用位置。它是有效的,因为Except
在内部使用了一个集合。
答案 3 :(得分:0)
allRestaurantMenus.Where(x => labelOfFoodsDesired.All(b => x.MenuItems.SelectMany(y => y.FoodItems.Select(a => a.Label)).Contains(b)));
通过将所有食物中的所有食物放入一个清单,然后对所有需要的食物进行检查,确认它们在该列表中。