5

IQueryable(Of 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#で問題ありません。

4

5 に答える 5

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にこれを行う方法を教える必要がないことを願っています。

于 2012-07-03T12:37:10.827 に答える
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;
});

そして最後にこれを呼び出すと、この結果が得られます。

職種結果

シンプルな、一種の...

于 2012-07-03T12:45:26.617 に答える
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
于 2012-07-03T13:51:34.810 に答える
1

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

}
于 2012-07-03T12:48:14.700 に答える
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();
}
于 2012-07-04T08:41:38.783 に答える