对于这种特定情况,适当的LINQ查询是什么?

时间:2016-02-02 12:00:45

标签: c# linq

鉴于以下两个类:

public class Apple
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Worm
{
    public int AppleId { get; set; }
    public int WormType { get; set; }
    public int HungerValue { get; set; }
}

所有蠕虫实例的AppleId等于随机存在的Apple.Id

public void DoLINQ(List<Apple> apples, List<Worm> worms, string targetAppleName, List<int> wormTypes )
{
    // Write LINQ Query here
}

我们如何编写Linq查询 找到&#39; apples&#39;中的所有元素,其名称&#39;匹配&#39; targetAppleName&#39; 和 (不是&#34;包含&#34; Wormtypes中给出的蠕虫类型的任何蠕虫   要么 只包含Hungervalue等于500的蠕虫?

请注意,Apple的实例实际上并不包含&#39;蠕虫的任何元素,因为这种关系是相反的。这也是使事情变得复杂的原因以及为什么更难以弄明白。

- 更新1 -

我尝试选择具有相同Id的多个苹果:

var query =
    from a in apples
    join w in worms
    on a.Id equals w.AppleId
    where (a.Name == targetAppleName) && (!wormTypes.Any(p => p == w.WormType) || w.HungerValue == 500)
    select a;

- 更新2 -

这更接近解决方案。这里我们使用两个查询然后合并结果:

var query =
    from a in apples
    join w in worms
    on a.Id equals w.AppleId
    where (a.Name == targetAppleName) && !wormTypes.Any(p => p == w.WormType)
    group a by a.Id into q
    select q;

var query2 =
    from a in apples
    join w in worms
    on a.Id equals w.AppleId
    where (a.Name == targetAppleName) && wormTypes.Any(p => p == w.WormType) && w.HungerValue == 500
    group a by a.Id into q
    select q;

var merged = query.Concat(query2).Distinct();

- 更新3-- 对于输入,我们希望LINQ查询使用方法中的参数,仅使用那些参数。 对于输出,我们想要所有满足上述条件的苹果。

4 个答案:

答案 0 :(得分:1)

var result = apples.Where(apple =>
    {
        var wormsInApple = worms.Where(worm => worm.AppleId == apple.Id);
        return apple.Name == targetAppleName 
            && (wormsInApple.Any(worm => wormTypes.Contains(worm.WormType)) == false
            || wormsInApple.All(worm => worm.HungerValue == 500));
    });

对于每个苹果,在该苹果中创建一系列蠕虫。只返回符合所需名称 AND 的苹果(不包含WormType中的蠕虫 OR 仅包含HungerValue为500的蠕虫)。

答案 1 :(得分:1)

如果要使用查询语法,可以使用let结构查找给定苹果的蠕虫:

var q = 
    from a in apples
    let ws = from w in worms where w.AppleId == a.Id select w
    where 
        (ws.All(w => w.HungerValue == 500)
        || ws.All(w => !wormTypes.Any(wt => wt == w.WormType))) 
        && a.Name == targetAppleName
    select a;

在方法链语法中,这相当于使用Select:

引入中间匿名对象
var q =
    apples.Select(a => new {a, ws = worms.Where(w => w.AppleId == a.Id)})
          .Where(t => (t.ws.All(w => w.HungerValue == 500)
                       || t.ws.All(w => wormTypes.All(wt => wt != w.WormType)))
                       && t.a.Name == targetAppleName).Select(t => t.a);

我不会完全称之为可读性: - )

答案 2 :(得分:0)

使用lambda看起来像这样:

var result = apples.Where(a => 
    a.Name == targetAppleName &&
    (worms.Any(w => w.AppleId == a.Id && w.HungerValue >= 500)) ||
     worms.All(w => w.AppleId != a.Id));

我认为lambda使代码看起来更清洁/更容易阅读,而且.Any().All()的使用效率比加入IMHO时更加有效...我还没有'用任何重要的数据对它进行测试,这里很难说出权威(加上,不会有那么多苹果......!)

BTW,这是整个代码。有点惊讶它对你不起作用。也许你错过了什么......?

public class Apple
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Worm
{
    public int AppleId { get; set; }
    public int WormType { get; set; }
    public int HungerValue { get; set; }
}

void Main()
{
    var apples = Enumerable.Range(1, 9).Select(e => new Apple { Id = e, Name = "Apple_" + e}).ToList();
    var worms = Enumerable.Range(1, 9).SelectMany(a =>
        Enumerable.Range(1, 5).Select((e, i) => new Worm { AppleId = a, WormType = e, HungerValue = i %2 == 0 ? a * e * 20 : 100 })).ToList();

    DoLINQ(apples, worms, "Apple_4", new[] {4, 5});

}

public void DoLINQ(IList apples, IList worms, string targetAppleName, IList wormTypes)
{
    // Write LINQ Query here
    var result = apples.Where(a => 
        a.Name == targetAppleName &&
        (worms.All(w => w.AppleId != a.Id) || worms.Any(w => w.AppleId == a.Id && w.HungerValue >= 500)));
    result.Dump();  // remark this out if you're not using LINQPad

    apples.Dump();  // remark this out if you're not using LINQPad
    worms.Dump();   // remark this out if you're not using LINQPad
}

答案 3 :(得分:-2)

我已经修改了您的查询但尚未经过测试但是让我们一起来试试吧。希望它能解决你的问题。

 var query =
   from a in apples
   join w in worms
   on a.Id equals w.AppleId into pt
   from w in pt.DefaultIfEmpty()
   where (a.Name == targetAppleName) && (!wormTypes.Any(p => p == w.WormType) || (w.HungerValue == 500))
   select a;

感谢。