简体   繁体   English

LINQ查询对象列表以获取基于多个字段的分布

[英]LINQ query on an object list to get distribution based on multiple fields

I have a list of bugs MyBugList created using the following class 我有一个使用以下类创建的错误MyBugList列表

internal class BugDetails
{
    public int Id { get; set; }
    public string State { get; set; }
    public string Severity { get; set; }
}

I wanted to group these bugs based on State and Severity . 我想根据StateSeverity对这些错误进行分组。 I used the following code to achieve it. 我使用以下代码来实现它。

var BugListGroup = (from bug in MyBugList
                             group bug by new
                             {
                                 bug.State,
                                 bug.Severity
                             } into grp
                             select new
                             {
                                 BugState = grp.Key.State,
                                 BugSeverity = grp.Key.Severity,
                                 BugCount = grp.Count()
                             }).OrderBy(x=> x.BugState).ToList();

This linq query gives me output like the following 这个linq查询给出了如下输出

Closed      Critical    40
Active      Critical    167
Closed      Medium      819
Closed      Low         323
Resolved    Medium      61
Resolved    Low         11
Closed      High        132
Active      Low         17
Active      Medium      88
Active      High        38
Resolved    High        4
Resolved    Critical    22
Deferred    High        11

However I would like to get an output like below 但是我想得到如下的输出

            Critical    High    Medium  Total
Closed      3           4       5       12
Active      5           4       5       14
Resolved    6           4       5       15
Deferred    1           4       5       10
Total       15          16      20      51

Is it possible to get this via a LINQ query on MyBugList or on BugListGroup 是否可以通过MyBugListBugListGroup上的LINQ查询获取此BugListGroup

I would like to get the output as a list so that I can be make it source a data grid. 我想将输出作为一个列表,以便我可以使它成为一个数据网格源。

Note: State and Severity values are dynamic and cannot be hard coded 注意:State和Severity值是动态的,不能进行硬编码

Below is my implementation with the help of answer provided by Dmitriy Zapevalov 以下是我在Dmitriy Zapevalov提供的答案的帮助下实施的

private void button1_Click(object sender, EventArgs e)
{
    var grouped = MyBugList.GroupBy(b => b.State).Select(stateGrp => stateGrp.GroupBy(b => b.Severity));

    //Setting DataGrid properties
    dataGridBug.Rows.Clear();
    dataGridBug.Columns.Clear();
    dataGridBug.DefaultCellStyle.NullValue = "0";
    dataGridBug.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;

    //Declaring DataGrid Styles
    var gridBackColor = Color.AliceBlue;
    var gridFontStyle = new Font(Font, FontStyle.Bold | FontStyle.Italic);

    //Declaring column and row Ids
    const string stateColumnId = "State";
    const string totalColumnId = "Total";
    const string totalRowId = "Total";

    //Adding first column
    dataGridBug.Columns.Add(stateColumnId, stateColumnId);
    dataGridBug.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;

    //Adding other columns
    foreach (var strSeverity in MyBugList.Select(b => b.Severity).Distinct())
    {
        dataGridBug.Columns.Add(strSeverity, strSeverity);
    }

    //Adding Total Column
    var totColPos = dataGridBug.Columns.Add(totalColumnId, totalColumnId);
    var totCol = dataGridBug.Columns[totColPos];

    //Adding data to grid
    foreach (var state in grouped)
    {
        var nRow = dataGridBug.Rows.Add();
        var severities = state as IList<IGrouping<string, BugDetails>> ?? state.ToList();
        dataGridBug.Rows[nRow].Cells[0].Value = severities.First().First().State;
        var sevCount = 0;
        foreach (var severity in severities)
        {
            dataGridBug.Rows[nRow].Cells[severity.Key].Value = severity.Count();
            sevCount += severity.Count();
        }
        dataGridBug.Rows[nRow].Cells[totalColumnId].Value = sevCount;
    }


    //Adding total row
    var totRowPos = dataGridBug.Rows.Add(totalRowId);
    var totRow = dataGridBug.Rows[totRowPos];

    //Adding data to total row
    for (var c = 1; c < dataGridBug.ColumnCount; c++)
    {
        var sum = 0;
        for (var i = 0; i < dataGridBug.Rows.Count; ++i)
        {
            sum += Convert.ToInt32(dataGridBug.Rows[i].Cells[c].Value);
        }
        dataGridBug.Rows[totRowPos].Cells[c].Value = sum;
    }

    //Styling total column
    totCol.DefaultCellStyle.BackColor = gridBackColor;
    totCol.DefaultCellStyle.Font = gridFontStyle;

    //Styling total row
    totRow.DefaultCellStyle.BackColor = gridBackColor;
    totRow.DefaultCellStyle.Font = gridFontStyle;
}


Output in the data grid will look like 数据网格中的输出看起来像

DataGrid输出

You can do this (it is dynamic solution, where you not need to hardcode anything). 你可以这样做(它是动态解决方案,你不需要硬编码任何东西)。 Details property of BugModel class contains all columns as Critical , High and so on: BugModel类的Details属性包含CriticalHigh等所有列:

public class BugModel
{
    public string BugState { get; set; }
    public Dictionary<string, int> Details { get; set; }
    public int Total { get { return Details.Sum(x => x.Value); } }
}

Solution: 解:

var result = (from bug in BugListGroup
              group bug by bug.BugState into sub
              select new BugModel
              {
                  BugState = sub.Key,
                  Details = sub.GroupBy(x => x.BugSeverity)
                             .ToDictionary(x => x.Key, x => x.Sum(y => y.BugCount))
              }).ToList();

I've done double grouping: 我做过双重分组:

class Program
{
    internal class BugDetails
    {
        public int Id { get; set; }
        public string State { get; set; }
        public string Severity { get; set; }
    }
    static void Main(string[] args)
    {
        var MyBugList = new BugDetails[]
        {
            new BugDetails() { Id = 1, State = "Active", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Critical" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Critical" },

            new BugDetails() { Id = 1, State = "Active", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Active", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Medium" },
            new BugDetails() { Id = 1, State = "Resolved", Severity = "Medium" },

            new BugDetails() { Id = 1, State = "Active", Severity = "High" },
            new BugDetails() { Id = 1, State = "Active", Severity = "High" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "High" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "High" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "High" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "High" },
            new BugDetails() { Id = 1, State = "Closed", Severity = "High" },
        };

        var grouped = MyBugList.GroupBy(b => b.State).
            Select(stateGrp => stateGrp.GroupBy(b => b.Severity));

        foreach (var state in grouped)
        {
            Console.Write("{0}: ", state.First().First().State);
            foreach (var severity in state)
            {
                Console.Write("{0}={1} ", severity.Key, severity.Count());
            }
            Console.WriteLine();
        }
    }
}

Output: 输出:

Active: Critical=1 Medium=2 High=2
Closed: Critical=3 Medium=2 High=5
Resolved: Critical=3 Medium=3

If you want to display data with DataGridView In that case you can create type dynamically with your own Properties set. 如果要使用DataGridView显示数据在这种情况下,您可以使用自己的属性集动态创建类型。 But this way is to complicated. 但这种方式很复杂。 The most simple (and more performance) way is to fill DataGridView manually: 最简单(也是更多性能)的方法是手动填充DataGridView:

private void button1_Click(object sender, EventArgs e)
{
    var grouped = MyBugList.GroupBy(b => b.State).
        Select(stateGrp => stateGrp.GroupBy(b => b.Severity));

    dataGridView1.Columns.Add("State", "State");
    foreach (var strSeverity in MyBugList.Select(b => b.Severity).Distinct())
        dataGridView1.Columns.Add(strSeverity, strSeverity);

    foreach (var state in grouped)
    {
        int nRow = dataGridView1.Rows.Add();
        dataGridView1.Rows[nRow].Cells[0].Value = state.First().First().State;
        foreach (var severity in state)
        {
            dataGridView1.Rows[nRow].Cells[severity.Key].Value = severity.Count();
        }
    }
}

The result: 结果: 在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM