I have the following code:
public class Alert
{
public string Alias { get; set; }
public int ServiceHours { get; set; }
public int TotalHoursDone { get; set; }
public int UserId { get; set; }
public int VehicleId { get; set; }
}
private static readonly List<Alert> AlertsToDo = new List<Alert>();
public void SomeFunction() {
// creates a dictionary of user ids as the key with their alerts
// (sorted by alias) as the value.
// Just one loop needed and no awkward state logic.
var alertsGrouped = AlertsToDo.Select(a => a.UserId)
.Distinct()
.ToDictionary(userId => userId,
userId => AlertsToDo.Where(a => a.User开发者_高级运维Id == userId)
.OrderBy(a => a.Alias)
.ToList());
}
So, I have a list of Alert objects. My LINQ query outputs a Dictionary the key of which is the UserId and the value of which is a List of Alerts for that UserId sorted by Alias.
I'm happy enough with the ingenuity of this query but am wondering if there's an easier way to do it? Specifically, I'm having to use a second query for every key to create the value List. Asking purely out of interest as this is fast enough for the job in hand.
There's a shorter approach that is more readable, using the Enumerable.GroupBy
method. For a small amount of data you most likely won't see a difference, whereas a large amount of data would change the performance. Your query first gets the distinct values, then filters items on UserId
. The grouping cuts down on those steps upfront. To know for sure you would need to profile.
Here's the query using grouping:
var query = AlertsToDo.GroupBy(a => a.UserId)
.ToDictionary(g => g.Key,
g => g.OrderBy(a => a.Alias).ToList());
Sorry about the delay, but as promised here are some test results.
The code I tested with:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class Alert
{
public string Alias { get; set; }
public int ServiceHours { get; set; }
public int TotalHoursDone { get; set; }
public int UserId { get; set; }
public int VehicleId { get; set; }
}
private static readonly List<Alert> AlertsToDo = new List<Alert>();
private void button1_Click(object sender, EventArgs e)
{
var rng = new Random();
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < 1000000; i++)
{
int random = rng.Next();
AlertsToDo.Add(new Alert
{
Alias = random.ToString(),
UserId = random
});
}
Console.WriteLine(@"Random generation: {0}", watch.ElapsedMilliseconds);
watch = new System.Diagnostics.Stopwatch();
watch.Start();
var alertsGrouped = AlertsToDo.Select(a => a.UserId)
.Distinct()
.ToDictionary(userId => userId,
userId => AlertsToDo.Where(a => a.UserId == userId)
.OrderBy(a => a.Alias)
.ToList());
watch.Stop();
Console.WriteLine(@"Mine: {0}", watch.ElapsedMilliseconds);
Console.WriteLine(alertsGrouped.Count);
watch = new System.Diagnostics.Stopwatch();
watch.Start();
alertsGrouped = AlertsToDo.GroupBy(a => a.UserId)
.ToDictionary(g => g.Key,
g => g.OrderBy(a => a.Alias).ToList());
watch.Stop();
Console.WriteLine(@"Ahmad's: {0}", watch.ElapsedMilliseconds);
Console.WriteLine(alertsGrouped.Count);
}
}
And the output:
Random generation: 769
Mine: 32164861 (over 8 hours)
999798
Ahmad's: 4133
999798
Unsurprisingly the GroupBy is quicker as my version performs a subquery, but just look at how much quicker!
So the winner is: Ahmad, obviously.
精彩评论