【问题标题】:How to determine is a value falls within a list of ranges?如何确定一个值是否属于范围列表?
【发布时间】:2019-01-13 11:06:09
【问题描述】:

我有一个包含最小值和最大值列表的输入类型。我需要做的是验证列表以确保最小值/最大值之间没有重叠。 DTO 与此类似

public class FeeDTO
{
    /// <summary>
    /// Gets or sets the fees.
    /// </summary>
    /// <value>
    /// The fees.
    /// </value>
    public List<IndividualFee> Fees { get; set; }
}

public class IndividualFee
{

    /// <summary>
    /// Gets or sets the minimum value.
    /// </summary>
    /// <value>
    /// The minimum value.
    /// </value>
    public Int32 MinValue { get; set; }

    /// <summary>
    /// Gets or sets the maximum value.
    /// </summary>
    /// <value>
    /// The maximum value.
    /// </value>
    public Int32 MaxValue { get; set; }
}

例如,这个输入就可以了,因为所有范围都是互斥的:

  • 范围 1:最小值 = 0 最大值 = 100
  • 范围 2:最小值 = 101 最大值 = 200
  • 范围 3:最小值 = 201 最大值 = 300

但是,在以下示例中,范围 3 的最小值落在范围 2 内

  • 范围 1:最小值 = 0 最大值 = 100
  • 范围 2:最小值 = 101 最大值 = 200
  • 范围 3:最小值 = 199 最大值 = 300

我的问题是,检测值之间的这种重叠的最佳方法是什么?

【问题讨论】:

  • 效率不高,但首先对其进行排序:fees.OrderBy(x =&gt; x.Max) 然后为每个检查下一个Min 是否大于此Max
  • 你有没有尝试过什么?为什么我们应该做你应该做的事情:思考和尝试?
  • 定义最好,最容易写?最容易使用?在大多数情况下运行速度最快?

标签: c# numbers integer


【解决方案1】:

例如,您可以创建一个自定义集合类型,在添加新项目时根据您的逻辑验证数据,例如:

public class FeeList : ICollection<IndividualFee>
{
    private readonly List<IndividualFee> _list = new List<IndividualFee>();

    public int Count => _list.Count;

    public bool IsReadOnly => false;

    public void Add(IndividualFee item)
    {
        if (item == null)
            throw new ArgumentNullException(nameof(item));
        if (item.MaxValue < item.MinValue)
            throw new ArgumentException("Overlaps", nameof(item));
        if (_list.Count > 0 && item.MinValue < _list[_list.Count - 1].MaxValue)
            throw new ArgumentException("Overlaps", nameof(item));
    }

    public void Clear() => _list.Clear();

    public bool Contains(IndividualFee item) => _list.Contains(item);

    public void CopyTo(IndividualFee[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);

    public IEnumerator<IndividualFee> GetEnumerator() => _list.GetEnumerator();

    public bool Remove(IndividualFee item) => _list.Remove(item);

    IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}

然后,您可以在 DTO 类中使用此自定义类型,而不是使用 List&lt;T&gt;

public class FeeDTO
{
    public FeeList Fees { get; set; }
}

这应该确保您不能将任何无效数据添加到 FeeDTO 对象而不引发异常。

如果您不想在验证失败时抛出异常,您可以将IsValid 属性添加到FeeList 并根据需要进行设置。

【讨论】:

    【解决方案2】:

    简单,Fiddle with tests here

    public static bool IsOverlap(IEnumerable<IndividualFee> fees)
    {
        var limit = int.MinValue;
        foreach (var fee in fees.OrderBy(f => f.MinValue))
        {
            if (fee.MinValue <= limit)
            {
                return true;
            }
    
            limit = fee.MinValue;
    
            if (fee.MaxValue <= limit)
            {
                return true;
            }
    
            limit = fee.MaxValue;
    
        }
    
        return false;
    }
    

    【讨论】:

      【解决方案3】:

      我认为解决这个问题最明显的方法是创建一个遍历每个最大值并将它们与每个最小值进行比较的 foreach。我们也可以使用一些 LINQ 使代码更紧凑,我相信可能会有更有效的算法,但这是我能想到的:

      foreach(IndividualFee fee in Fees){
          if(Fees.FirstOrDefault(f => f.MaxValue > fee.MinValue) != null){
              //Code for if the ranges are overlapping
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 2022-01-21
        • 2019-02-22
        • 2021-11-20
        相关资源
        最近更新 更多