【问题标题】:foreach loop with multiple logic conditions具有多个逻辑条件的 foreach 循环
【发布时间】:2016-07-22 16:56:18
【问题描述】:

我有一组需要迭代和处理的对象。到目前为止似乎很容易。但是,我有一些条件使它变得相当复杂。

这里有一些信息:

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

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

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

  • 第一阶段:上午 8:00 - 上午 9:30
  • 第 2 阶段:上午 10:00 - 上午 11:00
  • 第三阶段:上午 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++;

   }

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

感觉/看起来太复杂、太老套、太混乱了。有没有更好/现代的方法来做到这一点?

谢谢!

【问题讨论】:

  • 午夜后相位会循环吗?也就是说12:15am - 2:30am10:00pm - 11:50pm是否构成一次观看?
  • 如果 continueBlocking 做 X,否则做 X。X = X。那你为什么要使用 continueBlocking ?
  • 您是否考虑过使用 case 语句而不是多个 if 语句?

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


【解决方案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&lt;Schedule&gt; 添加项目。

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

【讨论】:

  • 我接受了您的回答,但使用上面的代码,它无法解析“开始”或“结束”符号。
【解决方案2】:

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

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 - 2016 年 7 月 22 日下午 12:30:00

2016 年 7 月 22 日下午 2:00:00 - 2016 年 7 月 22 日下午 4:00:00

2016 年 7 月 22 日下午 6:30:00 - 2016 年 7 月 22 日晚上 9:00:00

【讨论】:

    猜你喜欢
    • 2021-05-21
    • 1970-01-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 2017-12-10
    • 1970-01-01
    • 2021-12-28
    相关资源
    最近更新 更多