LINQ能实现这个吗?

6

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表中我只会得到包含所有tid列表值的那些kid,并与表ID进行比较。

此后预期结果是:

kid - 001
kid - 004

我尝试了下面的查询:

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

但是它返回所有匹配的结果,即使只有一个匹配也会给出结果,但我只想找出表ID与TID列表完全匹配的结果。 我的查询结果是:

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

请为此提供一个Linq查询建议。

LINQ是必须的吗?我可以用SQL查询提供解决方案。 - Nikhil Vartak
不,我想要用LINQ。 - KARAN
也许这个intersect的答案可以帮助。 - DanielV
5个回答

1
你可以先对 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 存在一些问题,如果你想要包含所有的 tid,而不是相等的,则可以使用 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));

1
为了实现你想要的,你首先需要按照 Kid 对你的数值进行 Group
Klist.GroupBy(k => k.Kid)

结果是一个不同的 Kid 列表及其相应的 Tableid
之后,我们需要选择所有其 Tableid 均与 tid 相同的组。这可以通过使用 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


0

这个工作得相当不错:

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 

0
另一种方法是:
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值。


0

有很多答案是使用 Except(Func<T, bool>).Any() 或者 Except(Func<T, bool>).Count() == 0,但这两种组合看起来应该只是 All(Func<T, bool>)

根据你的问题,你说当 kid 值存在于所有行中时,你只想要 kid 值。这是我会这样做的方法。

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

这将表格分组为包含 Kid 的集合。然后过滤掉不包含所有 tid 值的行。接着选择这些组的键,即 Kid


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接