在Dictionary <datetime,double =“”> </datetime,>中找到最接近的DateTime键

时间:2014-05-13 13:42:11

标签: c# linq datetime dictionary collections

我将DateTime的Date部分作为查找值,并且喜欢在类型Dictionary<DateTime, double>的字典中检索匹配值。请注意,DateTime键仅存储为日期部分。

我的问题是可能没有与我的查找值匹配的密钥。我当时喜欢做的是找到最近的 previous dateTime.Date键和匹配值。

现在,我知道词典没有按键排序。我可以使用SortedDictionary,但更喜欢使用Dictionary来解决特定原因,或者切换到List集合(可以预先排序)。我的问题是,在这种情况下你会建议做什么:在找到匹配的密钥之前保留Dictionary结构并递减查找值会更有效吗?或者使用列表集合并使用Linq会更好吗?每个词典包含大约5000个键/值对。此外,请注意我寻找一个高计算效率的解决方案,因为查找的频率非常高(可能数十万次(每次查找保证与以前的任何值不同)

3 个答案:

答案 0 :(得分:5)

由于您需要快速,我认为最好的方法是使用BinarySearch的结果。这需要List<T>已排序。

int result = myList.BinarySearch(targetDate);
if (result >= 0)
    return myList[result];
else
{
    int nextLarger = ~result;
    // return next smaller, or if that doesn't exist, the smallest one
    return myList[Math.Max(0, nextLarger - 1)];
}

应该可以创建一个结合了Dictionary<TKey,TValue>和排序List<TKey>的类,它仍然像Dictionary<TKey,TValue>一样序列化。序列化可能与在类中放置[JsonConverter(typeof(KeyValuePairConverter))]一样简单(在Json.NET中)。

为了完整性,如果其他人在将来读到这篇文章,如果速度不是很重要,你可以用这样的东西更简单地做到:

var result = myDict.Keys.Where(x => x < targetDate).Max();

答案 1 :(得分:2)

我会使用自定义结构和集合来存储这些信息:

public struct DateValue
{
    public DateValue(DateTime date, double val)
        : this()
    {
        this.Date = date;
        this.Value = val;
    }
    public DateTime Date { get; set; }
}

这是一个集合的可能实现,它包含所有DateValues并封装逻辑以返回最近的。它使用List.BinarySearch来查找它。如果找不到直接匹配,则使用BinarySearch的逻辑来检测最近的:

  

指定数组中指定值的索引(如果值为)   找到。如果未找到值且值小于一个或多个   数组中的元素,负数是按位补码   第一个元素的索引大于value。如果有价值   找不到,值大于数组中的任何元素,a   负数,是(的指数)的按位补数   最后一个元素加上1)。

public class DateValueCollection : List<DateValue>, IComparer<DateValue>
{
    public DateValueCollection() { }

    public DateValueCollection(IEnumerable<DateValue> dateValues, bool isOrdered)
    {
        if (isOrdered)
            base.AddRange(dateValues);
        else
            base.AddRange(dateValues.OrderBy(dv => dv.Date));
    }

    public DateValue GetNearest(DateTime date)
    {
        if (base.Count == 0)
            return default(DateValue);

        DateValue dv = new DateValue(date, 0);
        int index = base.BinarySearch(dv, this);

        if (index >= 0)
        {
            return base[index];
        }
        // If not found, List.BinarySearch returns the complement of the index
        index = ~index;

        DateValue[] all;
        if(index >= base.Count - 1)
        {
            // proposed index is last, check previous and last
            all = new[] { base[base.Count - 1], base[base.Count - 2] };
        }
        else if(index == 0)
        {
            // proposed index is first, check first and second
            all = new[] { base[index], base[index + 1] };
        }
        else
        {
            // return nearest DateValue from previous and this
            var thisDV = base[index];
            var prevDV = base[index - 1];
            all = new[]{ thisDV, prevDV };
        }
        return all.OrderBy(x => (x.Date - date).Duration()).First();
    }

    public int Compare(DateValue x, DateValue y)
    {
        return x.Date.CompareTo(y.Date);
    }
}

快速测试:

var dateVals = new[] { 
    new DateValue(DateTime.Today.AddDays(10), 1), new DateValue(DateTime.Today, 3), new DateValue(DateTime.Today.AddDays(4), 7) 
};
var dvCollection = new DateValueCollection(dateVals, false);
DateValue nearest = dvCollection.GetNearest(DateTime.Today.AddDays(1));

答案 2 :(得分:0)

为什么担心preomurley

做到这一点

然后,只有那么如果它慢,那么你有问题 使用分析器

进行测量

然后理解从哪个点开始尝试其他方式并对其进行分析。

答案是:如果你以任何方式做到这一点,并且没有性能问题,你只是节省了时间,并设法做了一些有用的事情来增加你一天的价值。

过早优化不仅毫无意义,你通常会对你应该去的地方完全错误。