我有一个IQueryable(Of Job)
,其中有Job,其中包括:
Property CreatedOn as DateTime
Property JobType as JobTypes
Enum JobTypes
JobType1
JobType2
JobType3
End Enum
我想要的是一个列表,按CreatedOn
排序,然后按JobType
分组计数
例如说我有(缩写日期)
11:00 JobType1
11:01 JobType2
11:02 JobType2
11:03 JobType2
11:04 JobType2
11:05 JobType3
11:06 JobType1
11:07 JobType1
我想要
JobType1 1
JobType2 4
JobType3 1
JobType1 2
我不知道如何在分组时考虑订购。有人能以正确的方式指出我吗?根据喜好,我更喜欢流利的语法。 VB.Net或C#很好。
答案 0 :(得分:3)
这个技巧很容易训练LinqToObjects:
public static IEnumerable<IGrouping<TKey, TSource>> GroupContiguous<TKey, TSource>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
bool firstIteration = true;
MyCustomGroupImplementation<TKey, TSource> currentGroup = null;
foreach (TSource item in source)
{
TKey key = keySelector(item);
if (firstIteration)
{
currentGroup = new MyCustomGroupImplementation<TKey, TSource>();
currentGroup.Key = key;
firstIteration = false;
}
else if (!key.Equals(currentGroup.Key))
{
yield return currentGroup;
currentGroup = new MyCustomGroupImplementation<TKey, TSource>();
currentGroup.Key = key;
}
currentGroup.Add(item);
}
if (currentGroup != null)
{
yield return currentGroup;
}
}
public class MyCustomGroupImplementation<TKey, TSource> : IGrouping<TKey, TSource>
{
//TODO implement IGrouping and Add
}
由
使用IEnumerable<IGrouping<JobType, Job> query = Jobs
.OrderBy(j => j.CreatedOn)
.GroupContiguous(j => j.JobType);
与任何旧的linq提供商一起“查看上一行”并不容易。我希望你不必教LinqToSql或LinqToEntities如何做到这一点。
答案 1 :(得分:2)
这是一种使用Aggregrate
方法的合理方法。
如果你从这样的JobTypes
列表开始:
var jobTypes = new []
{
JobTypes.JobType1,
JobTypes.JobType2,
JobTypes.JobType2,
JobTypes.JobType2,
JobTypes.JobType2,
JobTypes.JobType3,
JobTypes.JobType1,
JobTypes.JobType1,
};
您可以通过首先定义累加器来使用Aggregate
,如下所示:
var accumulator = new List<KeyValuePair<JobTypes, int>>()
{
new KeyValuePair<JobTypes, int>(jobTypes.First(), 0),
};
然后Aggregate
方法调用如下所示:
var results = jobTypes.Aggregate(accumulator, (a, x) =>
{
if (a.Last().Key == x)
{
a[a.Count - 1] =
new KeyValuePair<JobTypes, int>(x, a.Last().Value + 1);
}
else
{
a.Add(new KeyValuePair<JobTypes, int>(x, 1));
}
return a;
});
最后调用这个给你这个结果:
简单,有点......
答案 2 :(得分:2)
此更新版本使用子例程执行与以前相同的操作,但不需要额外的内部字段。 (我保留了我的早期版本,为避免使用Zip
例程,需要额外的OrDer
字段。)
Option Explicit On
Option Strict On
Option Infer On
Imports so11310237.JobTypes
Module so11310237
Enum JobTypes
JobType1
JobType2
JobType3
End Enum
Sub Main()
Dim data = {New With{.CO=#11:00#, .JT=JobType1, .OD=0},
New With{.CO=#11:03#, .JT=JobType2, .OD=0},
New With{.CO=#11:05#, .JT=JobType3, .OD=0},
New With{.CO=#11:02#, .JT=JobType2, .OD=0},
New With{.CO=#11:06#, .JT=JobType1, .OD=0},
New With{.CO=#11:01#, .JT=JobType2, .OD=0},
New With{.CO=#11:04#, .JT=JobType2, .OD=0},
New With{.CO=#11:07#, .JT=JobType1, .OD=0}}
' Check that there's any data to process
If Not data.Any Then Exit Sub
' Both versions include a normal ordering first.
Dim odata = From q In data Order By q.CO
' First version here (and variables reused below):
Dim ljt = odata.First.JT
Dim c = 0
For Each o In odata
If ljt <> o.JT Then
ljt = o.JT
c += 1
End If
o.OD = c
Next
For Each p In From q In data Group By r=q.JT, d=q.OD Into Count()
Console.WriteLine(p)
Next
Console.WriteLine()
' New version from here:
' Reset variables (still needed :-()
ljt = odata.First.JT
c = 0
For Each p In From q In odata Group By r=q.JT, d=IncIfNotEqual(c,q.JT,ljt) Into Count()
Console.WriteLine(p)
Next
End Sub
Function IncIfNotEqual(Of T)(ByRef c As Integer, ByVal Value As T, ByRef Cmp As T) As Integer
If Not Object.Equals(Value, Cmp) Then
Cmp = Value
c += 1
End If
Return c
End Function
End Module
答案 3 :(得分:1)
Non-Linq答案
鉴于
public enum JobTypes
{
JobType1,
JobType2,
JobType3
}
public class Job
{
public JobTypes JobType { get; set; }
public DateTime CreatedOn { get; set; }
}
public class JobSummary
{
public JobSummary(JobTypes jobType, long count)
{
this.JobType = jobType;
this.Count = count;
}
public JobTypes JobType { get; set; }
public long Count { get; set; }
}
然后你可以
private List<JobSummary> GetOrderedSummary(List<Job> collection)
{
var result = new List<JobSummary>();
if (!collection.Any())
{
return result;
}
var orderedCollection = collection.OrderBy(j => j.CreatedOn);
var temp = orderedCollection.First();
var count = 1;
foreach (var job in orderedCollection.Skip(1))
{
if (temp.JobType == job.JobType)
{
count++;
continue;
}
result.Add(new JobSummary(temp.JobType, count));
temp = job;
count = 1;
}
result.Add(new JobSummary(temp.JobType, count));
return result;
}
使用
private void DoSomething()
{
var collection = new List<Job>
{
new Job{JobType = JobTypes.JobType1, CreatedOn = DateTime.Now},
new Job{JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(1)},
new Job{JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(2)},
new Job{JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(3)},
new Job{JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(4)},
new Job{JobType = JobTypes.JobType3, CreatedOn = DateTime.Now.AddSeconds(5)},
new Job{JobType = JobTypes.JobType3, CreatedOn = DateTime.Now.AddSeconds(6)},
new Job{JobType = JobTypes.JobType1, CreatedOn = DateTime.Now.AddSeconds(7)},
new Job{JobType = JobTypes.JobType1, CreatedOn = DateTime.Now.AddSeconds(8)},
};
var summary = GetOrderedSummary(collection);
}
答案 4 :(得分:1)
Linq答案
public enum JobTypes
{
JobType1,
JobType2,
JobType3
}
static void Main(string[] args)
{
var collection = new[]
{
new {JobType = JobTypes.JobType1, CreatedOn = DateTime.Now},
new {JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(1)},
new {JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(2)},
new {JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(3)},
new {JobType = JobTypes.JobType2, CreatedOn = DateTime.Now.AddSeconds(4)},
new {JobType = JobTypes.JobType3, CreatedOn = DateTime.Now.AddSeconds(5)},
new {JobType = JobTypes.JobType1, CreatedOn = DateTime.Now.AddSeconds(7)},
new {JobType = JobTypes.JobType1, CreatedOn = DateTime.Now.AddSeconds(8)}
};
var orderedCollection = collection.OrderBy(job => job.CreatedOn);
var temp = orderedCollection.First().JobType;
var identifier = 0;
var summary = orderedCollection.Select(job =>
{
if (job.JobType == temp)
{
return new { JobType = job.JobType, Id = identifier };
}
temp = job.JobType;
return new { JobType = job.JobType, Id = ++identifier };
}).GroupBy(job => new { job.JobType, job.Id }).Select(job => new { JobType = job.Key.JobType, Count = job.Count() });
foreach (var sum in summary)
{
Console.WriteLine("JobType: {0}, Count: {1}", sum.JobType, sum.Count);
}
Console.ReadLine();
}