这在LINQ中是可能的吗?

本文关键字:LINQ 这在 | 更新日期: 2023-09-27 18:18:00

是否可以只返回与LINQ所有列表值匹配的值?

我有一个表klist,其中有两列可用:

Kid  -  Tableid  
001      1
001      2
002      1
003      3
004      1
004      2

和我有一个包含tid值的列表:

List<int> tid = new List<int>();
tid.Add(1);
tid.Add(2);

现在tid列表有1和2的值。

所以我的问题是,从klist表,我只会得到那些kid包含所有的tid列表值,并与表id比较。

之后的预期结果是

kid - 001
kid - 004

我试了下面的查询:

var lb = Klist.Where(t => tid .Contains(t.Tableid)).Select(k => k.Kid).Distinct().ToList();

返回所有匹配子,即使是单个匹配,也会给出结果,但我只想要那些匹配所有表id与tid list的子。我查询的结果是

kid - 001
kid - 002
kid - 003
kid - 004

请建议LINQ查询。

这在LINQ中是可能的吗?

为了实现你想要的,你首先需要通过Kid:

将你的值Group:
Klist.GroupBy(k => k.Kid)

结果是一个不同的Kid s及其对应的Tableid s的列表。

之后,我们需要选择所有Tableid s与tid s相同的组。这可以通过使用Except从两个列表中删除相同的元素来实现。然后检查是否没有剩余元素(即两个集合相等)。

.Where(g => tid.Except(g.Select(s => s.Tableid)).Count() == 0)

最后,获取其余组的Kid,它作为Key存储在组中:

.Select(g => g.Key);

如果我们将所有内容组合在一起,我们得到以下LINQ查询:

var lb = Klist.GroupBy(k => k.Kid).Where(g => tid.Except(g.Select(s => s.Tableid)).Count() == 0).Select(g => g.Key);

返回元素001004

您可以先将kList分组,然后将SequenceEqualtid一起使用,下面是一个示例

List<KeyValuePair<string, int>> kList = new List<KeyValuePair<string, int>>()
{
    new KeyValuePair<string, int>("001",1),
    new KeyValuePair<string, int>("001",2),
    new KeyValuePair<string, int>("002",1),
    new KeyValuePair<string, int>("003",3),
    new KeyValuePair<string, int>("004",1),
    new KeyValuePair<string, int>("004",2)
};
List<int> tid = new List<int>() { 1, 2 };
var query = kList.GroupBy(i => i.Key)
    .Where(g => g.Select(j => j.Value).Distinct().OrderBy(i => i).SequenceEqual(tid))
    .SelectMany(g => g.Select(x => x.Key)).Distinct();
Console.WriteLine(string.Join(",", query));

SequenceEqual可能有一些问题,如果你想要contains所有的tid,而不是Equal,那么你可以使用Intersect来代替SequenceEqual,代码是这样的

List<KeyValuePair<string, int>> kList = new List<KeyValuePair<string, int>>()
{
    new KeyValuePair<string, int>("001",1),
    new KeyValuePair<string, int>("001",2),
    new KeyValuePair<string, int>("002",1),
    new KeyValuePair<string, int>("001",3),//special one
    new KeyValuePair<string, int>("003",3),
    new KeyValuePair<string, int>("004",1),
    new KeyValuePair<string, int>("004",2)
};
List<int> tid = new List<int>() { 1, 2 };
var query = kList.GroupBy(i => i.Key)
    .Where(g => g.Select(j => j.Value).Distinct().Intersect(tid).Count()==tid.Count)
    .SelectMany(g => g.Select(x => x.Key)).Distinct();
Console.WriteLine(string.Join(",", query));

效果很好:

var kList = new []
{
    new { Kid = "001", Tableid = 1 },
    new { Kid = "001", Tableid = 2 },
    new { Kid = "002", Tableid = 1 },
    new { Kid = "003", Tableid = 3 },
    new { Kid = "004", Tableid = 1 },
    new { Kid = "004", Tableid = 2 },
};
var tid = new List<int>() { 1, 2, };
var lookup = kList.ToLookup(k => k.Tableid, k => k.Kid);

这产生:

1 => "001", "002", "004"
2 => "001", "004"
3 => "003"
var result = tid.Select(t => lookup[t]).Aggregate((ts0, ts1) => ts0.Intersect(ts1));

tid.Select(...)产生{ { "001", "002", "004" }, { "001", "004" } },然后Aggregate对这两个执行相交。

结果是:

001 
004 

另一种方法:

var list = kList.GroupBy(x => x.Kid)
            .Select(group => new 
             { 
                 Kid = group.FirstOrDefault().Kid, 
                 gtid = group.Select(groupItem => groupItem.Tableid).ToList() 
             })
            .Where(group => !group.gtid.Except(tid).Any() 
                 && !tid.Except(group.gtid).Any())
            .Select(group => group.Kid);

基本上像其他解决方案一样,您将kList中的数据分组,并使用kidTableid列表创建一个新列表。然后通过比较Tableid列表和tid列表来过滤新列表(确保一个列表中没有不在另一个列表中的列表项,反之亦然)。然后,选择kid值。

有很多答案是Except(Func<T, bool>).Any()Except(Func<T, bool>).Count() == 0,但这两个组合似乎都应该是All(Func<T, bool>)

根据你的问题,你说你只想要kid值,当它存在于Allkid值。这就是我要做的。

var lb = klist.GroupBy(lst => lst.Kid)
              .Where(t => tid.All(td => t.Select(x => x.TableId).Contains(td)))
              .Select(grp => grp.Key);

将表分组为包含Kid的集合。然后,它过滤掉不存在all tid值的行。然后为这些组选择键,即Kid