C# Group Store Open Days by Like Times



我有一个字典,它存储了一周中的每一天以及当天的开放/关闭时间。

Dictionary<string, string> hours = new Dictionary<string, string>();
hours.Add("M", String.Format("{0}-{1}", this.MondayOpen, this.MondayClosed));
hours.Add("T", String.Format("{0}-{1}", this.TuesdayOpen, this.TuesdayClosed));
hours.Add("W", String.Format("{0}-{1}", this.WednesdayOpen, this.WednesdayClosed));
hours.Add("Th", String.Format("{0}-{1}", this.ThursdayOpen, this.ThursdayClosed));
hours.Add("F", String.Format("{0}-{1}", this.FridayOpen, this.FridayClosed));
hours.Add("S", String.Format("{0}-{1}", this.SaturdayOpen, this.SaturdayClosed));
hours.Add("Su", String.Format("{0}-{1}", this.SundayOpen, this.SundayClosed));

我希望能够遍历字典并根据相似的时间对日期进行分组。例如,我想这样显示数据

M-Th 8:00 AM - 4:00 PM
F 8:00 AM - 6:00 PM
(leave Saturday and Sunday off if those days don't have open/close times)

我试过几种不同的方法,但都没能完全正确。这是我最近的一次尝试。

private string FormatHours2()
{
    StringBuilder sb = new StringBuilder();
    // add all of the hours to a dictionary
    Dictionary<string, string> hours = new Dictionary<string, string>();
    hours.Add("M", String.Format("{0}-{1}", this.MondayOpen, this.MondayClosed));
    hours.Add("T", String.Format("{0}-{1}", this.TuesdayOpen, this.TuesdayClosed));
    hours.Add("W", String.Format("{0}-{1}", this.WednesdayOpen, this.WednesdayClosed));
    hours.Add("Th", String.Format("{0}-{1}", this.ThursdayOpen, this.ThursdayClosed));
    hours.Add("F", String.Format("{0}-{1}", this.FridayOpen, this.FridayClosed));
    hours.Add("S", String.Format("{0}-{1}", this.SaturdayOpen, this.SaturdayClosed));
    hours.Add("Su", String.Format("{0}-{1}", this.SundayOpen, this.SundayClosed));
    // placeholder for the previous time range
    string prevValue = String.Empty;
    // inrun - indicates whether we are in a run of the same times.
    // firstTime - indicates whether this is the first time through the loop.
    bool inrun = false, firstTime = true;
    for (int i = 0; i < hours.Count; i++)
    {
        KeyValuePair<string, string> entry = hours.ElementAt(i);
        if (entry.Value != prevValue)
        {
            if (firstTime)
            {
                if (HasValue(entry.Value)) { sb.Append(entry.Key); }
            }
            else
            {
                if (!inrun)
                {
                    if (HasValue(prevValue)) { sb.Append(String.Format(" {0},", hours.ElementAt(i - 1).Value)); }
                    if (HasValue(entry.Value)) { sb.Append(entry.Key); }
                }
                else
                {
                    if (HasValue(prevValue))
                    {
                        sb.Append(String.Format("-{0} {1}", hours.ElementAt(i - 1).Key, hours.ElementAt(i - 1).Value));
                    }
                    if (HasValue(entry.Value)) { sb.Append(String.Format(",{0}", entry.Key)); }
                }
            }
            inrun = false;                    
        }
        else
        {
            inrun = true;
        }
        firstTime = false;
        prevValue = entry.Value;
        // if we're on the last iteration, write the value
        if (i == hours.Count() - 1)
        {
            if (inrun)
            {
                if (HasValue(entry.Value))
                {
                    sb.Append(String.Format("-{0} {1}", entry.Key, entry.Value));
                }
            }
            else
            {
                if (HasValue(prevValue))
                {
                    sb.Append(String.Format(" {0}", hours.ElementAt(i - 1).Value));
                }
            }
        }
    }

    return sb.ToString().TrimEnd(',');
}

这在所有情况下都很有效,除了在一周的中间几天关闭时,它不加逗号。

非常感谢任何帮助。如果有人知道更好的方法,我愿意接受建议。我甚至不需要用字典。

谢谢

你的代码很难阅读和维护,因为你试图同时做两件事:压缩数据(按相同的小时将天数合并成组)和用描述构建文本。

我会把这分成两个步骤,添加一些LINQ并重新组织一下。让我们从压缩问题开始。我们想要的是将列表分成具有相同工作时间的更小的集群。不幸的是,没有内置的LINQ方法来解决这个问题。当然我们也可以写一个:

public static class EnumerableExtensions
{
    public static IEnumerable<TResult> Compact<T, TKey, TResult>(
        this IEnumerable<T> source, 
        Func<T,TKey> keySelector, 
        Func<TKey, IEnumerable<T>, TResult> resultSelector) 
    {
        if (!source.Any())
            yield break;
        var comparer = EqualityComparer<TKey>.Default;
        TKey previousKey = keySelector(source.First());
        List<T> group = new List<T>() { source.First() };
        foreach (var item in source.Skip(1))
        {
            TKey currentKey = keySelector(item);                
            if (!comparer.Equals(previousKey, currentKey))
            {
                yield return resultSelector(previousKey, group);
                group = new List<T>();
            }
            group.Add(item);
            previousKey = currentKey;
        }
        if (group.Any())
        {
            yield return resultSelector(previousKey, group);
        }
    }
}

这段代码应该放在某个helper库中,这样就不会被查看代码的程序员看到。有了这个有用的方法,我们可以使用它:

var compact = hours.Compact(p => p.Value, 
                           (key, values) => new {
                               Hours = key,
                               Start = values.First().Key,
                               End = values.Last().Key
                           });

字典现在已经被压缩了(相对于KeyValuePair<>.Value,意思是工作时间),匿名对象的集合现在有了关于天的所有必要信息,你可以简单地用适当的格式string.Join它们:

Func<string, string, string> dayFormatter = 
    (first, second) => first == second ? first : string.Format("{0} - {1}", first, second);
var result = string.Join(", ", compact.Select(x => string.Format("{0} {1}",       
                                                      dayFormatter(x.Start, x.End),
                                                      x.Hours)));

注:对于示例数据,如:

string MondayOpen = "8:00 AM"; string MondayClosed = "4:00 PM";
string TuesdayOpen = "8:00 AM"; string TuesdayClosed = "4:00 PM";
string WednesdayOpen = "7:00 AM"; string WednesdayClosed = "3:00 PM";
string ThursdayOpen = "8:00 AM"; string ThursdayClosed = "4:00 PM";
string FridayOpen = "8:00 AM"; string FridayClosed = "4:00 PM";
string SaturdayOpen = "8:00 AM"; string SaturdayClosed = "2:00 PM";
string SundayOpen = "8:00 AM"; string SundayClosed = "2:00 PM";
Dictionary<string, string> hours = new Dictionary<string, string>();
hours.Add("M", String.Format("{0}-{1}", MondayOpen, MondayClosed));
hours.Add("T", String.Format("{0}-{1}", TuesdayOpen, TuesdayClosed));
hours.Add("W", String.Format("{0}-{1}", WednesdayOpen, WednesdayClosed));
hours.Add("Th", String.Format("{0}-{1}", ThursdayOpen, ThursdayClosed));
hours.Add("F", String.Format("{0}-{1}", FridayOpen, FridayClosed));
hours.Add("S", String.Format("{0}-{1}", SaturdayOpen, SaturdayClosed));
hours.Add("Su", String.Format("{0}-{1}", SundayOpen, SundayClosed));

上面的代码产生:

`"M - T 8:00 AM-4:00 PM, W 7:00 AM-3:00 PM, Th - F 8:00 AM-4:00 PM, S - Su 8:00 AM-2:00 PM"`

最新更新