4

以前にこの質問をして、datetime オブジェクトのコレクションを取得し、dayOfweek と時間でグループ化しました

要約すると、DateTimeのコレクションを取得します

List<DateTime> collectionOfDateTime = GetDateColletion();

そして、これを行うことにより、曜日と時刻でグループ化します

var byDayOfWeek = collectionOfDateTime.GroupBy(dt => dt.DayOfWeek + "-" + dt.Hour + "-" + dt.Minute);

したがって、この時点で、これらを週ごと (一貫した時間) にグループ化して完全に機能させています。

週ごとではなく月ごとにグループ化するという新しい要件があります。「月」というと、その月の同じ日ではなく、「毎月第一火曜日」のようなものです。

その月のロジック (月の第 1 火曜日、毎月第 2 金曜日など) に適合するすべてのアイテムをグループ化するために、グループで使用する「キー」を見つけようとしています。

例として、これらの日付を最初に持っていたとしましょう。

var date1 = new DateTime(2013, 1, 4).AddHours(8);  // This is the first Friday in Jan
var date2 = new DateTime(2013, 2, 1).AddHours(8);  // This is the first Friday in Feb
var date3 = new DateTime(2013, 1, 5).AddHours(3);  // This is the first Sat in Jan
var date4 = new DateTime(2013, 2, 2).AddHours(3);  // This is the first Sat in Feb
var date5 = new DateTime(2013, 2, 2).AddHours(6);  // This is the first Sat in Feb - different time

これらが元の配列に入った日付である場合、3 つのグループになるには groupby が必要です。

  • 最初のグループには、date1 と date2 が含まれます。
  • 2 番目のグループには、date3 と date4 が含まれます。
  • date5 は、異なる時間が与えられた他のグループのいずれにも一致しないため、それ自体になります。

とにかくその基準でグループ化することを誰かが提案できますか?

4

3 に答える 3

2

見た目よりも簡単だと思います:

var byDayOfMonth = from d in dates
                   let h = (d.Day / 7) + 1
                   group d by new { d.DayOfWeek, h } into g
                   select g;

ローカル変数h = (d.Day / 7) + 1DayOfWeek、その月内で実際にどの月であるかを設定します。

テスト用に実行し、例とまったく同じ2つのグループを受け取りました。そのグループのキーは次のとおりです。

{ DayOfWeek = Friday, h = 1 }
{ DayOfWeek = Saturday, h = 1 }

つまり、「月の最初の金曜日」と「月の最初の土曜日」のグループがあります。

d.Hourおよび/または必要d.Minuteに応じて、グループ化キーを簡単に拡張できます。

var byDayOfMonth = from d in dates
                   let h = (d.Day / 7) + 1
                   group d by new { d.DayOfWeek, h, d.Hour, d.Minute } into g
                   select g;

結果 (キーのみ):

{ DayOfWeek = Friday, h = 1, Hour = 8, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 3, Minute = 0 }
{ DayOfWeek = Saturday, h = 1, Hour = 6, Minute = 0 }
于 2013-03-02T13:24:38.230 に答える
0

これを行うにはおそらくもっと簡単な方法がありますが、これが私に来たものです:

あなたの質問から、「2 月の第 1 火曜日から 3 月の第 1 月曜日まで」などのすべてをグループ化して、これらの「月」スパンを可変日数である月に応じて取得する必要があることを収集します。彼らは始めました。その場合、これを年間通算日を使用して範囲に分割する必要があります。

Group by the First Wednesday of the Month 2013
Group 0 (0-1) 
All DayOfYear between 0 and 1 2013
Group 1 (2-36) 
The first Wednesday of the month: January is DayOfYear 2. 
The first Wednesday of the month: February is DayOfYear 37. 
etc.

したがって、最初の範囲は、 2 から 37 の範囲にあるため、 f (32) = 1 (DayOfYear は 32) となる関数fです。に該当し、その項目のインデックスをグループ番号として返します。

GetDateCollectionから最小および最大の日付を取得して全体の範囲を決定することにより、このテーブルを動的に作成できます。日付を取り巻くロジックはそれ自体が非常に複雑なトピックであるため、NodaTimeのようなライブラリ(具体的には算術ドキュメント) に頼り、最小日付から始めて、最初の適格な日 (つまり、 「月の最初の月曜日」) 0 からその日 - 1 までの範囲をグループ 0 として作成し、それをインデックス付きコレクションにプッシュします (ArrayList可能性が高い)。次に、その日付から月が変わるまでループしLocalDate.PlusWeeks(1)、新しい範囲を構築して、その範囲を同じインデックス付きコレクションにプッシュします。

新しい年に入るたびに、DayOfYear は毎年リセットされるため、インデックス付きコレクションを構築するときに、DayOfYear に 365 (または、前の年がうるう年の場合は 366) を追加する必要があります。

これで、DayOfYear に基づいて日を目的の単位にグループ化するテーブルとして機能する範囲のコレクションができました。

指定された日付の DayOfYear ( + [365|366] * xx は、比較する日付が最小年からの年数) をコレクション内の項目と比較して、その日が含まれる範囲が見つかるまでテーブルをトラバースする関数を作成し、そのアイテムのそのインデックスをグループ番号として返します。Func<DateTime,bool>(または、指定された がその範囲内にある場合に true を返す を各範囲にすることもできますDateTime。)

範囲のコレクションに代わるデータ構造は、長さが日付範囲の最小日から最大日までのすべての日に等しい ushort の配列であり、各日の値は割り当てられたグループ番号 (上記のように範囲で計算されます) です。 . これにより、グループ化のパフォーマンスが向上しますが、小さなデータセット (数百の日付のみ) で作業している場合、パフォーマンスは目立たない場合があります。

于 2013-03-02T04:34:15.233 に答える
-2

Linq を使用してグループ化するには、次のコードが役立ちます。

List<DateTime> collectionOfDateTime = GetDateColletion();
collectionOfDateTime.GroupBy(s => Convert.ToInt16(s.DayOfWeek) & s.Hour & s.Minute);
于 2013-03-02T12:23:04.693 に答える