В качестве ввода я использую приведенную ниже таблицу.
+--------+---------+-------+
| 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 баллов. Так, например, у Джона должно быть 3 звезды, тогда как у Ханны должна быть 1, а у Лилли должна быть 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);
Любые идеи?
дайте мне знать, отвечу вам работать или нет
это сработало для вас ???
@PranayRana Ваше решение разрешит мою проблему, большое спасибо! :) Мне было интересно, знаете ли вы, можно ли добавить оператор if в строку LINQ? Например, если количество баллов, равных 5, равно 3, выведите «Отлично», если 2, то «Хорошо» и так далее ... знаете ли вы, выполнимо ли это? Еще раз спасибо :)
@ Dr.Bake - обновил мой ответ, пожалуйста, попробуйте, пожалуйста, примите, если сработало для вас
Можете ли вы попробовать это, вам не нужна группа по счету, так как вы хотите суммировать их все, и для подсчета количества полученных 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 Ваше решение разрешит мою проблему, большое спасибо! :) Мне было интересно, знаете ли вы, можно ли добавить оператор if в строку LINQ? Например, если количество баллов, равных 5, равно 3, выведите «Отлично», если 2, то «Хорошо» и так далее ... знаете ли вы, выполнимо ли это? Еще раз спасибо :)
Если вы хотите использовать 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)}) ; Но не могли бы вы объяснить мне эту строчку? Я этого не совсем понял. еще раз спасибо!
Вы можете использовать предложение 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 => (Name: ag.Key, Score: ag.Sum(...)...));
Следующий код будет вам полезен,
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);
Count
принимает предикат, поэтому измените его наcount = g.Count(p => p.Score == 5)
. Полный код будет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(p => p.Score == 5) }).OrderByDescending(x => x.avg);