【问题标题】:How can I get a subset of a list based on a Datetime key in C#?如何根据 C# 中的日期时间键获取列表的子集?
【发布时间】:2010-10-08 09:41:33
【问题描述】:

我有一个描述在某个时间发生的事件的元素列表,时间被表示为对象上的 Datetime 属性“StartTime”。我现在希望提取这些事件的子集,其中包含放置在两个 DateTime 实例 A、B 之间的间隔中的那些元素,使得 StartTime >= A && StartTime

曾希望标准的 SortedList 类对键具有某种子集功能,但事实并非如此。有什么想法可以使用现有的框架类相对简单地归档,还是我必须基于 SortedList 进行某种自定义二进制搜索?

【问题讨论】:

    标签: c# sorting list


    【解决方案1】:

    如果可能,我会将实例保存在一个数组中,按 StartTime 排序,然后调用 BinarySearch 来确定数组中边界结束的索引。

    然后,鉴于此,您可以轻松快速地访问基于日期范围的子集。

    我已经设计了一个通用类,它也可以帮助你:

    public class BinarySearcher<T>
    {
        // Possibly passed to the call to BinarySort.
        private class ComparisonComparer : Comparer<T>
        {
            Comparison<T> comparison;
    
            internal static IComparer<T> Create(Comparison<T> comparison)
            {
                // If comparison is null, return the default comparer for T.
                if (comparison == null)
                {
                    // Return the default.
                    return Comparer<T>.Default;
                }
    
                // Return a new implementation.
                return new ComparisonComparer(comparison);
            }
    
            private ComparisonComparer(Comparison<T> comparison)
            {
                this.comparison = comparison;
            }
    
            public override int Compare(T x, T y)
            {
                return comparison(x, y);
            }
        }
    
        // The elements.
        T[] elements;
    
        // The IComparable implementation.
        IComparer<T> comparer;
    
        // Do not assume sorting.
        public BinarySearcher(IEnumerable<T> elements) : 
            this(elements, false, (IComparer<T>) null) { }
    
        // Use default comparer.
        public BinarySearcher(IEnumerable<T> elements, bool sorted) :
            this(elements, sorted, (IComparer<T>) null) { }
    
        // Assume no sorting.
        public BinarySearcher(IEnumerable<T> elements, 
            Comparison<T> comparer) :
                this(elements, false, 
                    ComparisonComparer.Create(comparer)) { }
    
        // Convert to IComparable<T>.
        public BinarySearcher(IEnumerable<T> elements, bool sorted, 
            Comparison<T> comparer) :
                this(elements, sorted, 
                    ComparisonComparer.Create(comparer)) { }
    
        // No sorting.
        public BinarySearcher(IEnumerable<T> elements, 
            IComparer<T> comparer) :
                this(elements, false, comparer) { }
    
        // Convert to array.
        public BinarySearcher(IEnumerable<T> elements, bool sorted, 
            IComparer<T> comparer) :
                this(elements.ToArray(), sorted, comparer) { }
    
        // Assume no sorting.
        public BinarySearcher(T[] elements) : this(elements, false) { }
    
        // Pass null for default comparer.
        public BinarySearcher(T[] elements, bool sorted) : 
            this(elements, sorted, (IComparer<T>) null) { }
    
        // Assume not sorted.
        public BinarySearcher(T[] elements, Comparison<T> comparer) :
            this(elements, false, ComparisonComparer.Create(comparer)) { }
    
        // Create IComparable<T> from Comparable<T>.
        public BinarySearcher(T[] elements, bool sorted, 
            Comparison<T> comparer) :
                this(elements, sorted, ComparisonComparer.Create(comparer)) { }
    
        // Assume the elements are not sorted.
        public BinarySearcher(T[] elements, IComparer<T> comparer) : 
            this(elements, false, comparer) { }
    
        public BinarySearcher(T[] elements, bool sorted, 
            IComparer<T> comparer)
        {
            // If the comparer is null, create the default one.
            if (comparer == null)
            {
                // Set to the default one.
                comparer = Comparer<T>.Default;
            }
    
            // Set the comparer.
            this.comparer = comparer;
    
            // Set the elements.  If they are sorted already, don't bother, 
            // otherwise, sort.
            if (!sorted)
            {
                // Sort.
                Array.Sort(elements, this.comparer);
            }
    
            // Set the elements.
            this.elements = elements;
        }
    
        public IEnumerable<T> Between(T from, T to)
        {
            // Find the index for the beginning.
            int index = Array.BinarySearch(this.elements, from, comparer);
    
            // Was the item found?
            bool found = (index >= 0);
    
            // If the item was not found, take the bitwise 
            // compliment to find out where it would be.
            if (!found)
            {
                // Take the bitwise compliment.
                index = ~index;
            }
    
            // If the item was found, cycle backwards from
            // the index while there are elements that are the same.
            if (found)
            {
                // Cycle backwards.
                for (; index >= 0 && 
                    comparer.Compare(from, elements[index]) == 0; 
                    --index) ;
    
                // Add one to the index, since this is on the element 
                // that didn't match the comparison.
                index++;
            }
    
            // Go forward now.
            for ( ; index < elements.Length; index++)
            {
                // Return while the comparison is true.
                if (comparer.Compare(elements[index], to) <= 0)
                {
                    // Return the element.
                    yield return elements[index];
                }
                else
                {
                    // Break
                    yield break;
                }
            }
        }
    }
    

    这里的大部分内容都是初始化类的助手,这需要一个比较两个项目的方法(这里采用IComparer&lt;T&gt;Comparable&lt;T&gt;)。该类还允许您接收已经排序的原始数组,以防您以后想自己在适当的位置添加/删除/更新元素(不必一直使用数组)。

    类的核心在于对Between的调用。它接受一个 from 值,该值将传递给 Array 类的静态 BinarySearch 方法,传递此实例使用的 IComparer 实现(如果没有传入,则假定默认值)。

    如果该值不存在,它会找出它在数组中存在的位置,然后在数组中有多个具有相同值的元素时向后循环。

    然后,它向前循环,在当前元素小于或等于 to 值时返回枚举中的项目。

    在您的特定情况下,假设您有这样的课程:

    public class Task
    {
        public string Name;
        public DateTime StartTime;
    }
    

    您可以执行以下操作:

    // Create tasks.
    Task[] tasks = 
    { 
        new Task() { Name = "Task 1", StartTime = new DateTime(2009, 02, 18) },
        new Task() { Name = "Task 2", StartTime = new DateTime(2009, 02, 16) },
        new Task() { Name = "Task 3", StartTime = new DateTime(2009, 02, 12) },
        new Task() { Name = "Task 4", StartTime = new DateTime(2009, 02, 11) },
        new Task() { Name = "Task 5", StartTime = new DateTime(2009, 02, 10) },
        new Task() { Name = "Task 6", StartTime = new DateTime(2009, 02, 01) },
        new Task() { Name = "Task 7", StartTime = new DateTime(2009, 02, 09) }
    };
    
    // Now create the indexer.
    BinarySearcher<Task> searcher = new BinarySearcher<Task>(tasks,
        (x, y) => Comparer<DateTime>.Default.Compare(x.StartTime, y.StartTime));
    
    foreach (Task t in searcher.Between(
        new Task() { StartTime = new DateTime(2009, 02, 13) },
        new Task() { StartTime = new DateTime(2009, 02, 10) }))
    {
        // Write.
        Console.WriteLine(t);
    }
    

    【讨论】:

    • 啊,谢谢。当没有找到精确匹配时,我不知道 BinarySearch(..) 返回了“最近的索引”。据我所知,在 SortedList 上没有直接的搜索方法。只需要弄清楚我是否可以直接访问 SortedList 的值数组,以重用插入逻辑。
    【解决方案2】:

    您是否使用自定义谓词检查了 Array.Find?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      相关资源
      最近更新 更多