Grouping while maintaining order in Linq

I have IQueryable(Of Job)where Job has, among other things:

Property CreatedOn as DateTime
Property JobType as JobTypes

Enum JobTypes
    JobType1
    JobType2
    JobType3
End Enum

What I want to get is a list sorted by CreatedOn, then grouped by JobTypewith a graph

Say I have (shortened dates)

11:00  JobType1
11:01  JobType2
11:02  JobType2
11:03  JobType2
11:04  JobType2
11:05  JobType3
11:06  JobType1
11:07  JobType1

I want to

JobType1 1
JobType2 4
JobType3 1
JobType1 2

I do not know how to take into account when grouping. can anyone point me to the right path? Optionally, I would prefer free Syntax. VB.Net or C # is fine.

+5
source share
5 answers

This trick is pretty easy to train 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
}

Is used

IEnumerable<IGrouping<JobType, Job> query = Jobs
  .OrderBy(j => j.CreatedOn)
  .GroupContiguous(j => j.JobType);

It is not so easy to do a “look at the previous line” with just any old linq provider. I hope you do not need to teach LinqToSql or LinqToEntities how to do this.

+3

, 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;
});

, , :

Job types results

, ...

+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 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
+2

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);

}
+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();
}
+1

All Articles