使用 linq c# 根据多个值进行左连接和计数

Left join and count based on multiple values using linq c#

我有 2 个列表。列表 A 包含此值,

status | level
-----------------
open   | low
open   | medium
open   | high
closed | low
closed | medium
closed | high

列表 B 包含此值,

task | status | level
------------------------
A    | open   | low
B    | open   | medium
C    | closed | high
D    | closed | low
E    | open   | low

我想做left join(列表A中的所有值必须在新列表中),并统计与状态相关的任务数。我也想要级别值,因为稍后将在我的代码中使用它。预期输出:

    status | level | count
    -------------------------
    open   | low      |  2
    open   | medium   |  1
    open   | high     |  0
    closed | low      |  1
    closed | medium   |  0
    closed | high     |  1

我知道这里有很多答案提供了编码的方法,但我仍然卡住了,因为我的代码不起作用,它似乎没有按方法进行分组,因为当我计数时,值显示的是所有状态。

var joined3 = (from id1 in joined
              join id2 in tr
              on new { lev = id1.Key.ToString(), stat = id1.Value.ToString() } equals new { lev = id2.Level.ToString(), stat = id2.Status.ToString() } into grouped
              from id2 in grouped.DefaultIfEmpty()
              group id2 by new {level = id1.Key, status = id1.Value } into grouped
              select new
              {
                    level = grouped.Key.level,
                    status = grouped.Key.status,
                    count =  grouped.Count()
              }).ToList();
var list1 = new List<Type1>
{
    new Type1() {Status = StatusEnum.Open, Level = LevelEnum.Low},
    new Type1() {Status = StatusEnum.Open, Level = LevelEnum.Medium},
    new Type1() {Status = StatusEnum.Open, Level = LevelEnum.High},
    new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.Low},
    new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.Medium},
    new Type1() {Status = StatusEnum.Closed, Level = LevelEnum.High}
};

var list2 = new List<Type2>
{
    new Type2() {TaskDescription = "A", Status = StatusEnum.Open, Level = LevelEnum.Low},
    new Type2() {TaskDescription = "B", Status = StatusEnum.Open, Level = LevelEnum.Medium},
    new Type2() {TaskDescription = "C", Status = StatusEnum.Closed, Level = LevelEnum.High},
    new Type2() {TaskDescription = "D", Status = StatusEnum.Closed, Level = LevelEnum.Low},
    new Type2() {TaskDescription = "E", Status = StatusEnum.Open, Level = LevelEnum.Low}
};

var list3 = new List<Type3>();
foreach (var t in list1)
{
    list3.Add(new Type3()
        {Level = t.Level, Status = t.Status, Count = list2.Count(x => x.Level == t.Level && x.Status == t.Status)});
}

foreach (var t in list3)
{
    Console.WriteLine($"{t.Status}/{t.Level}/{t.Count}");
}


class Type1
{
    public StatusEnum Status { get; set; }
    public LevelEnum Level { get; set; }
}

class Type2 : Type1
{
    public string TaskDescription { get; set; }
}

class Type3 : Type2
{
    public int Count { get; set; }
}

public enum StatusEnum
{
    Open,
    Closed
}

public enum LevelEnum
{
    Low,
    Medium,
    High
}

问题在于,由于 DefaultIfEmpty() 的 left-join 语义,您总是至少有一行。所以你需要给 Count()

添加一个谓词
var joined3 = (
    from id1 in joined
    join id2 in tr
       on new { lev = id1.Key, stat = id1.Value } equals new { lev = id2.Level, stat = id2.Status } into grouped
    from id2 in grouped.DefaultIfEmpty()
    group id2 by new {level = id1.Key, status = id1.Value } into grouped
    select new
    {
        level = grouped.Key.level,
        status = grouped.Key.status,
        count =  grouped.Count(id2 => id2.Key != null)
    }).ToList();

或者,更简单的方法是:不分组,而是使用其他列表的相关计数

var joined3 = (
    from id1 in joined
    select new
    {
        level = id1.level,
        status = id1.status,
        count =  tr.Count(id2 => id2.Key == id1.Key && id2.Value == id1.Value)
    }).ToList();

I see no reason to use ToString here, and it is likely to impact performance. Key and Value should be the same type on each list/table respectively.