Trying to find the top 3 properties of a POCO instance
Question
I have a simple POCO class that contains the student's scores.
For example:
Math - 83%
Engrish - 82%
Chemistry - 81%
Drama - 100%
etc..
Is there a way (using LINQ?) that I could figure out the top 3 properties ordered by score?
I'm assuming the final object will be an IList<T> of an anonymous type, which will have two fields.
- Name (the name of the property)
- Score (the decimal value).
The number of properties in the object ARE finite though :)
Any suggestions?
As an alternative answer, could this be done in a database instead?
Solution
Are you looking for something like this?
class Notes
{
public double Math{ get; set; }
public double English { get; set; }
public double Chemistry { get; set; }
public double Drama { get; set; }
public string IgnoreMePlease { get; set; }
}
class Program
{
static void PrintHighestNotes(Notes notes)
{
var pairs = from property in notes.GetType().GetProperties()
where property.PropertyType == typeof (double)
select new
{
Name = property.Name,
Value = (double) property.GetValue(notes, null)
};
var result = pairs.OrderByDescending(pair => pair.Value);
foreach (var pair in result)
Console.WriteLine("{0} = {1}", pair.Name, pair.Value);
}
static void Main(string[] args)
{
Notes notes = new Notes()
{
Chemistry = 0.10,
Math = 0.2,
Drama = 1,
English = 0.3,
IgnoreMePlease = "Ignore"
};
PrintHighestNotes(notes);
}
}
OTHER TIPS
Unless you already happen to have all the data in memory, it's more efficient to let the database select the correct data for you.
If you store the grades as fields in the database, you have to normalise it to make it possible to query. The best is to redesign the database and put the grades as rows in a separate table. The data should be in the fields of the tables, not as field names:
select top 3 GradeName, Grade
from Grades
where StudentId = 42
order by Grade desc
You can also normalise the data on the fly, but that is of course not nearly as efficient:
select top 3 GradeName, Grade
from (
select GradeName = 'Engrish', Grade = Engrish from Students where StudentId = 42
union all
select 'Drama', Drama from Students where StudentId = 42
union all
select 'Math', Math from Students where StudentId = 42
union all
select 'Chemistry', Chemistry from Students where StudentId = 42
) Grades
order by Grade desc
It would be simpler to use a dictionary with the subject as the key and the score as the value:
Dictionary<string, int> scores = new Dictionary<string, int>();
...
var top3Subjects = (from s in scores
orderby s.Value descending
select s).Take(3);
This returns an IEnumerable<KeyValuePair<string, int>>
, which you can use like this:
foreach (var s in top3Subjects)
{
Console.WriteLine("{0} : {1}", s.Key, s.Value);
}
It's not clear in your question if the scores are all separate properties or if they're some kind of list. If they're a list, this would work:
var topScores =
(from s in Scores
orderby s.Score descending
select new { s.Name, s.Score}).Take(3);