具有多个逻辑条件的foreach循环

时间:2016-07-22 16:56:19

标签: c# c#-4.0 foreach asp.net-4.5

我有一组对象需要迭代并完成。到目前为止似乎很容易。但是,我有一些条件让它变得相当复杂。

以下是一些信息:

该集合包含一堆具有行星相位时间的“Planet”对象。

如果两个阶段之间的时间跨度小于或等于30分钟,则行星观察时间会合并成块。

例如,这里有6个阶段时间:

  • 第1阶段:上午8:00 - 上午9:30
  • 第2阶段:上午10:00 - 上午11:00
  • 第3阶段:上午11:20至下午12:30
  • 第4阶段:下午2:00 - 下午4:00
  • 第5阶段:下午6:30 - 晚上7:30
  • 第6阶段:下午7:45 - 晚上9:00

根据上述数据,我们有以下几个块:

  • 阶段1至阶段3:一个连续观察块
  • 第4阶段:单独的查看区块
  • 阶段5和阶段6:一个连续观察块

数学:

  • (阶段2开始时间) - (阶段1结束时间)= 30分钟
  • (阶段3开始时间) - (阶段2结束时间)= 20分钟
  • (阶段4开始时间) - (阶段3结束时间)= 90分钟
  • (阶段5开始时间) - (阶段4结束时间)= 150分钟
  • (阶段6开始时间) - (阶段5结束时间)= 15分钟

到目前为止我的尝试失败了:

int i = 0;
bool continueBlocking = false;

    foreach (var p in GalaxySector)  //IEnumerable
    {
        //ensure that dates are not null
        if (p.StartDatePhase != null || p.EndDatePhase != null) {

            if (continueBlocking) {
                string planetName = p.Name;
                string planetCatalogId = p.CatalogId;
                datetime? StartPhase = p.StartDatePhase.Value;
                datetime? EndPhase = p.EndDatePhase.Value;
            } else {
                string planetName = p.Name;
                string planetCatalogId = p.CatalogId;
                datetime? StartPhase = p.StartDatePhase.Value;
                datetime? EndPhase = p.EndDatePhase.Value;
            }

            if (i < 2) {
         continue;  
            }

            TimeSpan? spanBetweenSections = StartPhase - EndPhase;


    if ( spanBetweenSections.Value.TotalMinues <= 30) {
               continueBlocking = true; 
               continue;

            } else {

                CreateSchedule(planetName, planetCatalogId, StartPhase, EndPhase);
                continueBlocking = false;
            }


      }

     i++;

   }

我在这个愚蠢的循环上花了好几个小时,我认为另一组眼睛会做得很好。

感觉/看起来太复杂,太老式,太混乱。有没有更好/现代的方式来做这件事?

谢谢!

2 个答案:

答案 0 :(得分:1)

假设这些是日期,而不仅仅是一天中的某些时间,您可以执行以下操作

var galaxySector = new List<PlanetPhase>
{
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 8, 0, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 9, 30, 0)
    },
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 10, 0, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 11, 0, 0)
    },
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 11, 20, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 12, 30, 0)
    },
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 14, 0, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 16, 0, 0)
    },
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 18, 30, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 19, 30, 0)
    },
    new PlanetPhase
    {
        Name = "Saturn",
        StartDatePhase = new DateTime(2016, 7, 22, 19, 45, 0),
        EndDatePhase = new DateTime(2016, 7, 22, 21, 0, 0)
    },
};


PlanetPhase previous = null;
int groupon = 0;
var results = galaxySector.GroupBy(p => p.Name)
    .Select(grp => new
    {
        PlanetName = grp.Key,
        Phases = grp.OrderBy(p => p.StartDatePhase)
            .Select(p =>
            {
                if (previous != null
                    && p.StartDatePhase - previous.EndDatePhase > TimeSpan.FromMinutes(30))
                {
                    groupon++;
                }

                previous = p;

                return new
                {
                    groupOn = groupon,
                    p.StartDatePhase,
                    p.EndDatePhase
                };
            })
            .GroupBy(x => x.groupOn)
            .Select(g => new
            {
                Start = g.Min(x => x.StartDatePhase),
                End = g.Max(x => x.EndDatePhase)
            })
            .ToList()
    });

foreach (var r in results)
{
    Console.WriteLine(r.PlanetName);
    foreach (var p in r.Phases)
        Console.WriteLine($"\t{p.Start} - {p.End}");
}

那将输出

  

土星

     

2016/7/22 8:00:00 - 7/22/2016 12:30:00 PM

     

2016/7/22 2:00:00 PM - 7/22/2016 4:00:00 PM

     

2016/7/22 6:30:00 - 7/22/2016 9:00:00 PM

答案 1 :(得分:1)

如果使用yield return将多个循环打包到可枚举返回方法中,则可以非常方便地进行分组:

private static readonly TimeSpan HalfHour = TimeSpan.Parse("0:30");

private static IEnumerable<Schedule> Group(IList<GalaxySector> all) {
    // Protect from division by zero
    if (all.Count == 0) {
        yield break;
    }
    // Find initial location
    var pos = 0;
    while (pos < all.Count) {
        var prior = (pos + all.Count - 1) % all.Count;
        if (all[prior].End+HalfHour >= all[pos].Begin) {
            pos++;
        } else {
            break;
        }
    }
    // Protect from wrap-around when all items belong to a single window
    pos = pos % all.Count;
    // Start grouping items together
    var stop = pos;
    do {
        var start = pos;
        var next = (pos+1) % all.Count;
        while (next != stop && all[pos].End+HalfHour >= all[next].Begin) {
            pos = next;
            next = (pos+1) % all.Count;
        }
        yield return new Schedule {Begin = all[start].Begin, End = all[pos].End};
        pos = next;
    } while (pos != stop);
}

上面的代码在午夜(demo)执行“环绕”。

方法相对简单:第一个循环通过向后退一步找到开始迭代的位置,以便在循环之后计划是连续的。第二个循环记住起始位置,并一次前进一步,检查窗口是否比半小时分开。一旦找到足够大的中断,或者当我们再次到达起点时,第二个循环就会停止。

如果您不想使用yield return,可以将项目添加到List<Schedule>来替换它。

var all = new GalaxySector[] {
    new GalaxySector {Begin=TimeSpan.Parse("0:15"), End=TimeSpan.Parse("2:30")}
,   new GalaxySector {Begin=TimeSpan.Parse("2:45"), End=TimeSpan.Parse("3:30")}
,   new GalaxySector {Begin=TimeSpan.Parse("8:00"), End=TimeSpan.Parse("9:30")}
,   new GalaxySector {Begin=TimeSpan.Parse("10:00"), End=TimeSpan.Parse("11:00")}
,   new GalaxySector {Begin=TimeSpan.Parse("11:20"), End=TimeSpan.Parse("12:30")}
,   new GalaxySector {Begin=TimeSpan.Parse("14:00"), End=TimeSpan.Parse("16:00")}
,   new GalaxySector {Begin=TimeSpan.Parse("18:30"), End=TimeSpan.Parse("19:30")}
,   new GalaxySector {Begin=TimeSpan.Parse("19:45"), End=TimeSpan.Parse("21:00")}
,   new GalaxySector {Begin=TimeSpan.Parse("22:00"), End=TimeSpan.Parse("23:50")}
};
foreach (var sched in Group(all)) {
    Console.WriteLine("{0}..{1}", sched.Begin, sched.End);
}

输出:

08:00:00..12:30:00
14:00:00..16:00:00
18:30:00..21:00:00
22:00:00..03:30:00