根据日期时间筛选大型列表

6

我有以下的Items类:

public class Item
{
   public Item()
   {
   }

   public string Id {get; set;}
   public string Name {get; set;}
   public string Price {get; set;}
   public DateTime CreatedDate {get; set;}

}

那么,在我的代码中,我有一个包含许多类型为 Item 的项目的List<Item> items,我的问题是,根据以下情况,您建议使用什么方法/实践来对基于创建日期对列表中的项目进行排序/过滤:

  • 所有创建日期早于日期x的项目
  • 所有创建日期晚于日期x的项目
  • 所有创建日期在日期x和日期y之间的项目

P.S. 如果我也提到时间呢?比如,日期x时间y之前/之后/之间的项目?


我在基本的If条件语句中尝试了DateTime.Compare...所以想知道什么是最优方法... - David Dury
这看起来很像一份作业,但我不确定:P - King King
DateTime重载了>>=等运算符时,我不会称之为DateTime.Compare有帮助。 - Jon Skeet
2
如果使用Linq进行线性搜索的答案很多,如果足够快(只有测试才能告诉你),那就没问题了。如果列表非常长并且需要进行大量搜索,而且列表不会经常更改,那么将其按CreatedDate排序并执行二进制搜索以查找所需项可能是值得的。但首先尝试其中一个Linq答案(如果不需要二进制搜索,我不会浪费时间编写使用二进制搜索的答案;) - Matthew Watson
考虑到LINQ的惰性求值和LINQ-to-Objects运算符的非缓存工作,这就像是给他一把手枪,告诉他“请朝那个方向开洞”。 - xanatos
@xanatos:我不明白为什么……我肯定会从一个简单的基于LINQ的解决方案开始,然后再考虑优化。除非他要对结果进行多次迭代,否则实现结果是没有意义的……如果你想实现结果,那很容易做到。 - Jon Skeet
7个回答

9
您可以使用 LINQ
var beforeDateX = items
    .Where(i => i.CreatedDate.Date < DateX); // remove the .Date if you want to include the time
var afterDateX = items
    .Where(i => i.CreatedDate.Date > DateX);
var betweenDates = items
    .Where(i => i.CreatedDate.Date >= DateX && i.CreatedDate.Date <= DateY);

您可以使用foreach或像ToList这样的方法来执行查询并实现结果的实体化。
foreach(Item i in beforeDateX)
    Console.WriteLine("Name:{0} CreatedDate:{1}", i.Name, i.CreatedAt);

2

使用Linq:

var itemsBefore = items.Where(i => i.CreatedDate <= timeBefore);
var itemsAfter = items.Where(i => i.CreatedDate >= timeAfter);
var itemsBetween = items.Where(i => i.CreatedDate >= timeStart && i.CreatedDate <= timeEnd);

订购流程

var ordrered = items.OrderBy(i => i.CreatedDate);

2

如果您有一个List<>,我建议:


List<Item> itemsBefore = items.FindAll(i => i.CreatedDate <= timeBefore);
List<Item> itemsAfter = items.FindAll(i => i.CreatedDate >= timeAfter);
List<Item> itemsBetween = items.FindAll(i => i.CreatedDate >= timeStart && i.CreatedDate <= timeEnd);

我建议的和其他人建议的有微小的区别。

.Where 方法不会“缓存”返回的列表,所以如果你执行以下操作:

var filtered = items.Where(condition);

foreach (var item in filtered)
{
}

foreach (var item in filtered)
{
}

您的整个列表将被解析两次,以搜索使条件为真的项。为了解决这个“问题”(有时可能是一个问题),您可以在.Where()之后添加.ToList()

List<>.FindAll()返回一个只包含所选项的新List<>。因此,您可以枚举它多少次都可以,因为它已经被“实例化”了。


1
所有的LINQ方法都很好,但是它们会迭代列表3次。如果有大量的项目,那么也许老式的方法会更有效(也就是说,如果你想同时使用这三种情况,否则LINQ答案是正确的选择):
List<Item> before = new List<Item>();
List<Item> after = new List<Item>();
List<Item> between = new List<Item>();

foreach (var item in Items)
{
  if (item.CreatedDate <= timeBefore)
  {
    before.Add(item);
  }
  else if (item.CreatedDate >= timeAfter)
  {
    after.Add(item);
  }
  else
  {
    between.Add(item);
  }
}

1
你可以使用LINQ Where
static void Main(string[] args)
{
    Item item1 = new Item() { CreatedDate = new DateTime(2010, 11, 10), Id = "1", Name = "foo1", Price = "10.00" };
    Item item2 = new Item() { CreatedDate = new DateTime(2010, 11, 11), Id = "2", Name = "foo2", Price = "11.00" };
    Item item3 = new Item() { CreatedDate = new DateTime(2010, 11, 12), Id = "3", Name = "foo3", Price = "12.00" };
    Item item4 = new Item() { CreatedDate = new DateTime(2010, 11, 13), Id = "4", Name = "foo4", Price = "13.00" };
    Item item5 = new Item() { CreatedDate = new DateTime(2010, 11, 14), Id = "5", Name = "foo5", Price = "14.00" };
    Item item6 = new Item() { CreatedDate = new DateTime(2010, 11, 15), Id = "6", Name = "foo6", Price = "15.00" };
    Item item7 = new Item() { CreatedDate = new DateTime(2010, 11, 16), Id = "7", Name = "foo7", Price = "16.00" };
    Item item8 = new Item() { CreatedDate = new DateTime(2010, 11, 17), Id = "8", Name = "foo8", Price = "17.00" };

    List<Item> items = new List<Item>();
    items.Add(item1);
    items.Add(item2);
    items.Add(item3);
    items.Add(item4);
    items.Add(item5);
    items.Add(item6);
    items.Add(item7);
    items.Add(item8);

    List<Item> filtered = ItemsBeforeDate(items, new DateTime(2010, 11, 16));
    foreach (Item i in filtered)
    {
        Console.Write(i.Name);
    }

    Console.Read();
}

public static List<Item> ItemsBeforeDate(List<Item> items, DateTime beforeDate)
{
    return items.Where(i => i.CreatedDate < beforeDate).ToList();
}

public static List<Item> ItemsAfterDate(List<Item> items, DateTime afterDate)
{
    return items.Where(i => i.CreatedDate > afterDate).ToList();
}

public static List<Item> ItemsBetweenDates(List<Item> items, DateTime startDate, DateTime endDate)
{
    return items.Where(i => i.CreatedDate >= startDate && i.CreatedDate <= endDate).ToList();
}

打印:

foo1 foo2 foo3 foo4 foo5 foo6


0

在我看来,第三种方式是可以的。

但是,如果您不想使用过滤器,可以在检索列表时实现分页。因为如果您设置了一个较大的日期范围,您将无法解决性能问题。


0

你需要查看可枚举方法

用于过滤使用Where

list.Where(x=>x.CreatedDate < yourDate).ToList();

关于 订购 的事项

list.OrderBy(x=>x.CreatedDate).ToList();

我会在执行where子句之前进行订购,以提高分支预测的友好性! :) - Moo-Juice
我只是在告诉他做事情的方法。 - Ehsan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接