【问题标题】:Group by column to count duplicated values from another column in LINQ WPF按列分组以计算 LINQ WPF 中另一列的重复值
【发布时间】:2018-05-02 11:55:15
【问题描述】:

我有下表作为我的输入。

+--------+---------+-------+
|  Name  | Course  | Score |
+--------+---------+-------+
| John   | Math    |     5 |
| Hannah | Math    |     4 |
| Lilly  | Math    |     5 |
| John   | Science |     5 |
| Hannah | Science |     5 |
| Lilly  | Science |     5 |
| John   | Social  |     5 |
| Hannah | Social  |     4 |
| Lilly  | Social  |     3 |
+--------+---------+-------+

作为输出,我想要一个表格来显示姓名、总分、平均分和星级。星星将是学生得分 5/5 的次数。例如,John 应该有 3 星,而 Hannah 应该有 1 星,而 Lilly 应该有 2 星。基本上,我希望将下表作为我的输出:

+--------+-------+---------+------+
|  Name  | Score | Average | Star |
+--------+-------+---------+------+
| John   |    15 |       5 |    3 |
| Hannah |    13 |     4.3 |    1 |
| Lilly  |    13 |     4.3 |    2 |
+--------+-------+---------+------+

我能够得到分数和平均分,但我就是不知道如何做星星部分。以编程方式,我认为我需要按名称分组,然后计算重复的数字,我认为应该在 linq 中完成,但我不知道该怎么做:

这是我的代码:

public string Name { get; private set; }
public int Score { get; set; }
public decimal Average { get; set; }
public decimal Count { get; set; }

public Person(string name, int score, decimal avg, int count)
{
    Name = name;
    Score = score;
    Average = avg;
    Count = count;
}
public ObservableCollection<Person> Persons { get; set; }
filepath = scoresFile; 

public MainWindow()
{

    InitializeComponent();
    LoadTable();
}

private void datagrid_Sorting(object sender, DataGridSortingEventArgs e)
{
    if (e.Column.Header.Equals("Name") || e.Column.Header.Equals("Score"))
    {
        e.Handled = true;
    }
}

public void LoadTable()
{
    var lines = File.ReadAllLines(filepath);
    Persons = new ObservableCollection<Person>();
    var data = lines.Select(line =>
    {

        var column = line.Split(',');
        int c = column[1].Count(); 
              var name = column[1];
        int score = int.Parse(column[3]);
        decimal avg = decimal.Parse(column[3]);
        int count = 0; 
        return new { name, score, avg, count};

    }
    );
    var groupedData = data.GroupBy(p => p.name)
            .Select((g, i) => new { 
                    num= 0, name = g.Key, 
                    score = g.Sum(p => p.score), 
                    avg = decimal.Round(g.Average(p => p.avg), 1), 
                    count = g.Count() })
            .OrderByDescending(x => x.avg);

    var persons = groupedData.Select((p, i) => new Person(i + 1, p.name, p.score, p.avg, p.count));


    foreach (var person in persons)
    {
        Persons.Add(person);
    }

    datagrid.ItemsSource = Persons;  
}

我一直在尝试对 linq 行进行一些修改,但我认为这不是一个好主意:

        var groupedData = data.GroupBy(p => p.name, s=> s.score )
              .Where(p => p.Count() > 5).Select((g, i) => new { 
                    num= 0, 
                    name = g.Key, 
                    rank = i + 1, 
                    score = g.Sum(p => p.score), 
                    avg = decimal.Round(g.Average(p => p.avg), 1), 
                    count = g.Count() })
              .OrderByDescending(x => x.avg);

然后我想添加以下行并调用 num,但这也没有用。

       var num = data.GroupBy(p => p.name, p =p.score).Any(g => g.Count() > 1);

有什么想法吗?

【问题讨论】:

  • Count 接受谓词,因此将其更改为 count = g.Count(p =&gt; p.Score == 5)。完整代码为var groupedData = data.GroupBy(p =&gt; p.name).Select((g, i) =&gt; new { num= 0, name = g.Key, score = g.Sum(p =&gt; p.score), avg = decimal.Round(g.Average(p =&gt; p.avg), 1), count = g.Count(p =&gt; p.Score == 5) }).OrderByDescending(x =&gt; x.avg);
  • 让我知道答案是否适合您
  • 这对你有用吗???
  • @PranayRana 您的解决方案解决了我的问题,非常感谢! :) 我想知道您是否知道是否可以在 LINQ 行中添加 if 语句?例如,如果等于 5 的分数计数为 3,则打印“Excellent”,如果它是 2,则打印“good”等等。你知道这是否可行吗?再次感谢:)
  • @Dr.Bake - 更新了我的答案,请尝试一下,如果对你有用,请接受

标签: c# wpf linq


【解决方案1】:

您可以在计数之前使用 where 子句。 试试下面的查询

        var Output = StudentCourseDetails.GroupBy(a => a.Name).Select(ag => new
        {
            Name = ag.Key,
            Score = ag.Sum( i => i.Score),
            Average = ag.Average(i => i.Score),
            Star = ag.Where(i=> i.Score == 5).Count()
        });

【讨论】:

  • 如果您将 Traget .NET-Version 设置为 4.7 以上,您可以使用...Select(ag =&gt; (Name: ag.Key, Score: ag.Sum(...)...));
【解决方案2】:

以下代码将对您有所帮助,

var result = (from s in data
              group s by  s.Name 
              into grp 
              select new {
                 name =  grp.Key,
                 score = grp.Sum(s=> s.Score),
                 avg =   decimal.Round(grp.Average(s =>Convert.ToDecimal(s.Score)),1,MidpointRounding.AwayFromZero),
                 star =  grp.Where(s=> s.Score == 5).Count()
              }).ToList();
Console.WriteLine(result);

或者

var result =  data.GroupBy(s=>s.Name).Select(g=>new{g.Key,score = g.Sum(s=> s.Score),avg= decimal.Round(g.Average(s =>Convert.ToDecimal(s.Score)),1,MidpointRounding.AwayFromZero),star =  g.Where(s=> s.Score == 5).Count()});
Console.WriteLine(result);

【讨论】:

    【解决方案3】:

    你能试试这个吗,你不需要对分数进行分组,因为你想把所有这些都加起来,并且要计算收到的 5 星的数量,你只需要计算它

    var data = from std in students
     group by std.Name
     into grp 
     select new {
       name = grp.key,
       score = grp.Sum(s=> s.Score),
       avg = decimal.Round(grp.Average(s => s.avg), 1),
       star = grp.Count(s=> s.Score == 5)
       Report = ReviewText(grp.Count(s=> s.Score == 5) )
     };
    
    private string ReviewText(int count)
    {
      if(count >= 3)
       return "Excellent";
      else if (count ==2)
          return "Good";
      else 
          return string.Empty;
    }
    

    或者其他方式

    var Output = students.GroupBy(std => std.Name)
                 .Select(grp => new
                 {
                   Name = grp.Key,
                   Score = grp.Sum( i => i.Score),
                   Average = decimal.Round(grp.Average(s => s.avr),1),
                   Star = grp.Count(i=> i.Score == 5)
                });
    

    【讨论】:

    • 为什么用.ToList()强制数据实现?
    • @MarkSchultheiss - 没有得到你...但如果它与 ToList() 相关,我添加了 ToList() 只是例如,如果他想要立即获得数据
    • @MarkSchultheiss - 我知道,它只是举例,基本上我想向 OP 展示他如何解决计数问题,这里与 ToList() 无关..现在删除
    • @PranayRana 您的解决方案解决了我的问题,非常感谢! :) 我想知道您是否知道是否可以在 LINQ 行中添加 if 语句?例如,如果等于 5 的分数是 3,那么如果是 2,则打印“Excellent”,然后打印“good”等等。你知道这是否可行吗?再次感谢:)
    【解决方案4】:

    如果您只想使用 LINQ 来解决此问题,以下内容将有所帮助 -

     var grp = emp.GroupBy(p => p.Name).Select((g) => new { Name = g.Key, Total = g.Sum(p=>p.Score),
                Star = g.All(p=>p.Score==5)?3:(g.Count(p=>p.Score==5)==2?2:1)});
    

    【讨论】:

    • 感谢您的回答 :) Star = g.All(p=>p.Score==5)?3:(g.Count(p=>p.Score==5)== 2?2:1)});但是你能向我解释一下这条线吗?我不太明白。再次感谢!
    猜你喜欢
    • 2018-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    相关资源
    最近更新 更多