2017-09-12 5 views
2

これはおそらく初心者の質問ですが、ここにはあります。DayOfWeekリストで一番近い曜日を見つけよう

日曜日(土曜日、日曜日、月曜日、&金曜日など、水曜日の&の可能性があります)に追加されるタイプのDayOfWeekリストがあります。

このリストでは、これをDatetimeパラメーターと比較し、DayTimeパラメーターがDayOfWeekリストに最も近い曜日を見つけ、その曜日に基づいてDateTimeパラメーターに日数を追加します。

たとえば、渡されるDateTimeパラメータが日曜日で、DayOfWeekリストに水曜日と土曜日が含まれる場合、パラメータはリストに最も近いので土曜日に戻す必要があります。

同様に、リストに日曜日、月曜日、土曜日があり、渡されたパラメータが木曜日の場合、パラメータは土曜日に移動する必要があります。

最後に、パラメータがリストの2週間の等間隔であり(水曜日が渡され、月曜日と金曜日がリストにある...または日曜日が渡され、火曜日と金曜日がリストにある)パラメータは次の最も近い曜日に移動する必要があります(最初のケースでは、2番目のケースでは金曜日と火曜日になります)。

それは私が何か行うことができますint型に日付に渡され、その方法から次に近い曜日の距離を変換するには、(少なくとも私にとっては)理想的である:

passedInDate = passedInDate.AddDays(dayOfWeekDistance); 
return passedInDate; 

をしかし、私は提案に開放されています。

int dayOfWeekDistance = targetDayOfWeekList.Min(x => (x - passedInDate)); 

しかし無駄に:

は私のようなLINQ文を試してみました。私が紛失しているファンキーなLINQ文がいくつかあります。

私が仕事につかない主な項目は、日付が渡された日曜日が日曜日で、リストの最も近い曜日が土曜日の場合は、日曜日から戻ってくる日付です(同様に、渡された日付が月曜日で、最も近い曜日が金曜日の場合、日付は金曜日までずっと移動する必要があります)。

私が何かを見逃してしまった場合はお知らせください。

すべての援助を歓迎します!ありがとう。

+0

私はこれをテストしたが、このを見て時間を持っていなかった[質問](https://stackoverflow.com/questions/20056048/linq-lambda-式 - 最も近い週の曜日 - 現在の日付 - 現在) – aaronR

+0

2つの日付の差異であるTimeSpanを計算しています。 DateTimeのデフォルトの時刻は深夜です。最初の質問はあなたが何時を比較しているのですか?真夜中か別の時間ですか? – jdweng

+0

深夜になります。 – wibby35

答えて

1

ヘルパー関数を使用すると、LINQを使用することができます。

ヘルパー関数は、2つのDOWs間の順方向の日数を計算するユーティリティ関数を使用して、週の最も近い日を計算します。

public int MinDOWDistance(DayOfWeek dow1, DayOfWeek dow2) { 
    int FwdDaysDiff(int idow1, int idow2) => idow2 - idow1 + ((idow1 > idow2) ? 7 : 0); 
    int fwd12 = FwdDaysDiff((int)dow1, (int)dow2); 
    int fwd21 = FwdDaysDiff((int)dow2, (int)dow1); 
    return fwd12 < fwd21 ? fwd12 : -fwd21; 
} 

次に、あなたがリストに最寄りのDOWを見つけて、右を返すことができますLINQでAggregateを使用して移動(および方向)する日数:

public int DaysToClosestDOW(DayOfWeek dow1, List<DayOfWeek> dowList) { 
    return dowList.Select(dow => { 
            var cdow = MinDOWDistance(dow1, dow); 
            return new { dow, dist = cdow, absdist = Math.Abs(cdow) }; 
           }) 
        .Aggregate((g1, g2) => (g1.absdist < g2.absdist) ? g1 : ((g1.absdist == g2.absdist) ? ((g1.dist > 0) ? g1 : g2) : g2)).dist; 
} 

は、すでにそれを知っているので、私はヘルパー関数からabsdistを返すようにタプルを使用することができ、私に起こりました。その後、私はLINQでTupleを使用することができます。

public (int dist, int absdist) MinDOWDistance(DayOfWeek dow1, DayOfWeek dow2) { 
    int FwdDaysDiff(int idow1, int idow2) => idow2 - idow1 + ((idow1 > idow2) ? 7 : 0); 
    int fwd12 = FwdDaysDiff((int)dow1, (int)dow2); 
    int fwd21 = FwdDaysDiff((int)dow2, (int)dow1); 
    if (fwd12 < fwd21) 
     return (fwd12, fwd12); 
    else 
     return (-fwd21, fwd21); 
} 

public int DaysToClosestDOW(DayOfWeek dow1, List<DayOfWeek> dowList) { 
    return dowList.Select(dow => MinDOWDistance(dow1, dow)) 
        .Aggregate((g1, g2) => (g1.absdist < g2.absdist) ? g1 : ((g1.absdist == g2.absdist) ? ((g1.dist > 0) ? g1 : g2) : g2)).dist; 
} 
3

問題をいくつかの小さな部分に分割します。

注:すべての以下のメソッドは、この

public static class DayOfWeekExtensions 
{ 
} 

ファーストのようなクラスの中に置くことになっている、あなたはDayOfWeekenumでそれが最初に定義されている間Sundayは、週の最後の日になりたいです。レッツもピボットを与えられた機能を追加

public static int OffsetTo(this DayOfWeek source, DayOfWeek target) 
{ 
    return source.GetIndex() - target.GetIndex(); 
} 

と:2つのDayOfWeek値の間に続いて、我々は(オフセット)までの距離を計算する関数が必要

public static int GetIndex(this DayOfWeek source) 
{ 
    return source == DayOfWeek.Sunday ? 6 : (int)source - 1; 
} 

:だから占め機能を作ってみよう2つのDayOfWeek値(自分の前方の優先ルールを適用)2の最も近い値を選択します。

public static DayOfWeek Closest(this DayOfWeek pivot, DayOfWeek first, DayOfWeek second) 
{ 
    int comp = Math.Abs(first.OffsetTo(pivot)).CompareTo(Math.Abs(second.OffsetTo(pivot))); 
    return comp < 0 || (comp == 0 && first.GetIndex() > pivot.GetIndex()) ? first : second; 
} 

は、今、私たちはどのメソッドを実装する準備ができていますシーケンスから最も近い日を見つけます。 !

public static DayOfWeek? Closest(this IEnumerable<DayOfWeek> source, DayOfWeek target) 
{ 
    if (!source.Any()) return null; 
    return source.Aggregate((first, second) => target.Closest(first, second)); 
} 

最後に、最も近い距離を計算する関数を追加してみましょう:

public static int ClosestDistance(this IEnumerable<DayOfWeek> source, DayOfWeek target) 
{ 
    return source.Closest(target)?.OffsetTo(target) ?? 0; 
} 

そして、我々は最終的には(使用して実装したものです:) LINQ Aggregate方法ここでは、多くの方法で実施することができますされます。小さな単純な再利用可能なユーティリティクラスを作成しました。

あなたのケースでの使用は、次のようになります。

int dayOfWeekDistance = targetDayOfWeekList.ClosestDistance(passedInDate.DayOfWeek); 

UPDATE:それはあなたの要件が異なっていることが判明しました。

同じ原則を適用するには、まず、前方優先ルールを適用して、週2日の間の前方および後方距離の最小値を計算する関数が必要です。それは基本的に何

public static int MinDistanceTo(this DayOfWeek from, DayOfWeek to) 
{ 
    int dist = to - from; 
    return dist >= 4 ? dist - 7 : dist <= -4 ? dist + 7 : dist; 
} 

-3..3包含範囲内の値に可能-6..6包括範囲から値を変換することです。

その後、我々は(それもMinとカスタム比較演算で実現することができる)Select + Aggregateを使用することによって、問題のメソッドを実装しますちょうど1より多くの機能を、必要があります。それは基本的に2つの絶対距離を比較し、再び前方の優先ルールを適用します。

public static int MinDistanceTo(this DayOfWeek from, IEnumerable<DayOfWeek> to) 
{ 
    if (!to.Any()) return 0; 
    return to.Select(x => from.MinDistanceTo(x)).Aggregate((dist1, dist2) => 
    { 
     if (dist1 == dist2) return dist1; 
     int comp = Math.Abs(dist1).CompareTo(Math.Abs(dist2)); 
     return comp < 0 || (comp == 0 && dist1 > 0) ? dist1 : dist2; 
    }); 
} 

と使用方法は次のようになります。

int dayOfWeekDistance = passedInDate.DayOfWeek.MinDistanceTo(targetDayOfWeekList); 
+0

これはほとんどの部分で機能しました!私には1つの質問がある。私は日曜日を最後の曜日にしたいかどうか分からないので、日付が渡された日が月曜日で、最も近い曜日が土曜日(または金曜日)の場合、日付は事実上土曜日(または金曜日)に...もしそれが意味をなさないならば。たとえば、リストに火曜日と金曜日が含まれていて、渡された日付が火曜日にバンプされた日曜日である場合(これは、等距離の前方ルールについて説明するために)、あなたの答えを変更できますか?それが私の唯一の批判です。そうでなければそれは完全に動作します! – wibby35

+0

優秀な説明! –

+0

@ wibby35よろしくお願いします。私はあなたのコメントに応じて答えを再訪し、更新しました。残念ながら、それは最初の思想とは全く異なるアプローチを必要としたので、最終的にNetMageによって投稿された別の回答に近づくようになりました(同じでなければ、異なる実装/コーディングスタイル)。私は他の答えをテストしませんでしたが、それがうまくいくかどうかは、最初に正しい方向に向かうので、正しいことを受け入れるべきだと思います。 –

関連する問題