从 12 个月的布尔值列表中创建日期范围字符串

Creating date range string from list of 12 month booleans

我有一个包含 12 个布尔属性的 c# class;

一月可用, 可用二月, 可用三月, 可用四月...等

这个 class 的每个实例都可以有任意数量的它们为真,通常它们 运行 在一个序列中。 IE。 AvailableFeb - AvailableApr 将为 true,然后 none 其他。有时只有一个 bool 为真,即仅在一个月内可用。有时它们都会,即全年可用。

有时,这很棘手,它们会有两个范围,即 2 月至 4 月和 8 月至 10 月可用。

我正在尝试编写一个函数来 return 一个字符串来表示所做的选择。

我只选择了 1 个布尔值 return(例如):"Feb only".

所选范围(例如):"Jan-Mar".

多个范围(例如):"Jan-Mar, Aug-Nov".

混合单一和范围(例如):"Jan-Mar, Sep"。

全部选中:"Year round"。

范围是连续月份被指定为真,例如一月、二月、三月为真应导致一月至三月。

我试过使用带有简单条件检查的循环,但它很乱,我无法正确处理,我很遗憾将它们存储为单独的属性而不是 int 数组,但我现在坚持这样做。我想知道是否有一种方法可以使用反射然后循环将 bool 存储在另一个 属性 中。到目前为止还没有运气。非常感谢任何帮助!

谢谢

因为您不能像这样简单地遍历成员,所以您可以创建一些属性来通过在属性中使用订单号来检测那些布尔值。例如

class Year
{
    [Month(0)]
    public bool AvaialableJan { // ... }

    [Month(1)]
    public bool AvaialableFeb { // ... }
}

你可以通过在列表中反射并搜索序列来获取它们。看看:Reflection - get attribute name and value on property

但更好的解决方案是在没有反射和一种 ranges:

的情况下工作
public class Month
{
    public string Name { //... }
}

public class Range
{
    public List<Month> Months //...
}

public class Year
{
    public List<Range> Ranges //...
}

但这两种解决方案都应该有效。

你可以这样不用反思:

string FormatMonths(MyClass myObject)
{
    return FormatMonths(
        myObject.AvailableJan,
        myObject.AvailableFeb,
        myObject.AvailableMar,
        myObject.AvailableApr,
        myObject.AvailableMay,
        myObject.AvailableJun,
        myObject.AvailableJul,
        myObject.AvailableAug,
        myObject.AvailableSep,
        myObject.AvailableOct,
        myObject.AvailableNov,
        myObject.AvailableDec);
}


private static string[] months = new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

string FormatMonthRange(int startMonth, int endMonth, StringBuilder sb)
{
    if (startMonth == 0 && endMonth == 11)
        sb.Append("Year round");
    else if (endMonth == 11 && sb[0] == 'J' && sb[1] == 'a')
    {
        // this deals with wrap around from December to January:
        if (sb.Length > 3 && sb[3] == '-')
            sb.Remove(0, 4);
        sb.Insert(0, months[startMonth] + "-");
    }
    else
    {
        if (sb.Length > 0)
            sb.Append(", ");
        sb.Append(months[startMonth]);
        if (startMonth != endMonth)
            sb.Append("-").Append(months[endMonth]);
    }
}

string FormatMonths(params bool[] monthBools)
{
    var sb = new StringBuilder();
    int rangeStart = -1;
    for (int i = 0; i < monthBools.Length; i++)
    {
        if (monthBools[i])
        {
            if (rangeStart < 0)
                rangeStart = i;
        }
        else
        {
            if (rangeStart >= 0)
            {
                FormatMonthRange(rangeStart, i - 1, sb);
            }
            rangeStart = -1;
        }
    }
    if (rangeStart >= 0)
        FormatMonthRange(rangeStart, monthBools.Length - 1, sb);
    if (sb.Length == 3)
        sb.Append(" only");
    return sb.ToString();
}

如果你想稍微改变一下你的class,那就按照这个方法做吧!

您可以拥有 12 个属性并将它们映射到单个 int 数组中。所以你有长度为 12 的 int 数组。当 intArray[0] = 1 意味着第一个月可用。如果 intArray[0] = 0 表示第一个月不可用,依此类推。

您将此 int 数组转换为字符串,以 base-16 表示每个月的基于 1 的索引,但如果它不可用,则用 0 代替。稍后我们使用字典通过给出月份的编号来获取月份的名称。

示例 1:

"003456000a00"

这意味着 3th,4th,5th,6tha = 10th 个月可用。然后使用正则表达式解析它并找到匹配项。正则表达式模式将为 [^0]+。表示它匹配除 0 以外的任何字符。

因此正则表达式将为我们提供此匹配项。

Match 1 : 3456
Match 2 : a

第一个匹配长度超过 1 意味着它是一个月份范围。所以我们取第一个字符和最后一个字符。我们加入了 -。这里将是 36。所以匹配1应该变成

Mar-Jun

第二个匹配长度只有1意味着它是一个月。所以应该变成

Oct

因为我们有两个匹配,所以我们用 , 加入它们,最后输出是 Mar-Jun , Oct

示例 2:

"020000000000"

匹配

Match 1 : 2

因为我们只有 1 个匹配项并且匹配项长度为 1,所以这应该变成

Feb Only

示例 3:

"023456780000"

匹配

Match 1 : 234567

这只是一场比赛,但这场比赛的长度不止一场。所以我们只需要 27 并用 -.

加入它们
Feb-Jul

例4:

"123456789abc"

匹配

Match 1 : 123456789abc

如您所见,我们有所有月份。这场比赛的长度是12所以它应该是

Year round

例5:

"123456000abc"

匹配

Match 1 : 123456
Match 2 : abc

这里有两场比赛。它可以是 Jan-Jun , Oct-Dec,但更好的表示(如您在评论中提到的)是 Oct-Jun。那应该是abc123456。因此,我们检查最后一场比赛是否以 c 结束,而第一场比赛是否以 1 开始,然后我们将最后一场比赛与第一场比赛合并。

Oct-Jun

代码:

如你所见,它会变得简单。

internal class AvailableYear
{
    private readonly int[] _available;
    private static readonly Regex MatchTrue = new Regex("[^0]+");
    private static readonly Dictionary<string, string> GetName = new Dictionary<string, string>
    {
        {"1","Jan" },
        {"2","Feb" },
        {"3","Mar" },
        {"4","Apr" },
        {"5","May" },
        {"6","Jun" },
        {"7","Jul" },
        {"8","Aug" },
        {"9","Sep" },
        {"a","Oct" },
        {"b","Nov" },
        {"c","Dec" },
    };

    public AvailableYear(params int[] available)
    {
        if (available.Length > 12) throw new IndexOutOfRangeException("given parameters should not exceed 12 months.");

        _available = available;
    }

    public int AvaialableJan
    {
        get { return _available[0]; }
        set { _available[0] = value; }
    }

    public int AvailableFeb
    {
        get { return _available[1]; }
        set { _available[1] = value; }
    }
    public int AvailableMar
    {
        get { return _available[2]; }
        set { _available[2] = value; }
    }
    public int AvailableApr
    {
        get { return _available[3]; }
        set { _available[3] = value; }
    }
    public int AvaialableMay
    {
        get { return _available[4]; }
        set { _available[4] = value; }
    }
    public int AvaialableJun
    {
        get { return _available[5]; }
        set { _available[5] = value; }
    }
    public int AvaialableJul
    {
        get { return _available[6]; }
        set { _available[6] = value; }
    }
    public int AvaialableAug
    {
        get { return _available[7]; }
        set { _available[7] = value; }
    }
    public int AvaialableSep
    {
        get { return _available[8]; }
        set { _available[8] = value; }
    }
    public int AvaialableOct
    {
        get { return _available[9]; }
        set { _available[9] = value; }
    }
    public int AvaialableNov
    {
        get { return _available[10]; }
        set { _available[10] = value; }
    }
    public int AvaialableDec
    {
        get { return _available[11]; }
        set { _available[11] = value; }
    }

    public override string ToString()
    {
        string values = string.Join("", _available.Select((x, i) => x == 0 ? "0" : Convert.ToString(i + 1, 16)));
        var matches = MatchTrue.Matches(values).Cast<Match>().Select(x => x.Value).ToList();

        if (matches.Count == 0)
        {
            return "None";
        }
        if (matches[0].Length == 12)
        {
            return "Year round";
        }
        if (matches.Count == 1 && matches[0].Length == 1)
        {
            return GetName[matches[0]] + " Only";
        }
        else
        {
            if (matches.First().StartsWith("1") && matches.Last().EndsWith("c"))
            {
                matches[0] = matches.Last() + matches.First();
                matches.RemoveAt(matches.Count - 1);
            }

            List<string> output = new List<string>();

            foreach (var match in matches)
            {
                if (match.Length == 1)
                {
                    output.Add(GetName[match]);
                }
                else
                {
                    output.Add(GetName[match.First().ToString()] + "-" +
                               GetName[match.Last().ToString()]);
                }
            }

            return string.Join(", ", output);
        }
    }
}

这是测试。

static void Main()
{
    AvailableYear ay = new AvailableYear(1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0);
    Console.WriteLine(ay.ToString());

    // Output : Jan , Apr-Jul , Nov
}

更新:

如果你想在构造函数中分配 bool 值,你可以将构造函数更改为此。

public AvailableYear(params bool[] available)
{
    if (available.Length > 12) throw new IndexOutOfRangeException("given parameters should not exceed 12 months.");

    _available = available.Select(Convert.ToInt32).ToArray();
}

然后像这样创建实例。不用每次都写 Convert.ToInt32。

return new AvailableYear(AvailableJan, AvailableFeb, AvailableMar...., AvailableDec).ToString();