2010-08-20 54 views
0

我有一个包含两个字符串和一个DateTime的对象的List<>。我想创建另一个只包含最后一个唯一项目的对象列表,使用两个字符串作为键和最后一个DateTime值。在SQL中,请考虑以下内容:List <>上的递归循环导致计算器溢出

SELECT col1, col2, MAX(datetime) FROM table GROUP BY col1, col2 

这给出了col1,col2和最后一个datetime的唯一列表。所以..我试图用两个列表的代码来做到这一点。其中有一个重复项,它解析并抓取最后一个唯一项目以填充第二个列表。

我拥有的数据集非常庞大,只需通过重复列表,然后检查项目是否在唯一列表中,如果它没有添加它,如果是,比较日期等。所以我想我可以通过递归遍历重复列表并获取唯一项目,找到它们的最大日期时间,并在我循环时删除非最大值列表,从而使我的重复列表变得越来越小,从而加快速度。 (我希望你仍然跟着我..)

所以无论如何。我写了一个带有两个列表的递归循环,但是当我循环时,我在第3000次迭代中得到了一个System.StackOverflowException

这是我的代码。想象一下ListWithDuplicates充满了数据。实际的ListDataItem有更多我遗漏的属性。但我的主要问题是为什么我不能通过这种方式循环通过public list而不会导致StackOverflowException

using System; 
using System.Net; 
using System.IO; 
using System.Collections.Generic; 
using System.Linq; 

public class RecursionTest 
{ 
    public List<listDataItem> ListWithDuplicates { get; set; } 
    public List<listDataItem> ListWithUniques { get; set; } 

    public RecursionTest() 
    { 
     Process(); 
    } 

    public void Process() 
    { 
     int rowcount = 0; 
     int duplicates = 0; 
     int total = 0; 
     RecursiveLoopForUnique(ref rowcount, ref duplicates, ref total, "", ""); 
    } 

    private void RecursiveLoopForUnique(ref int rowcount, ref int duplicates, ref int total, string col1, string col2) 
    { 
     if (rowcount > 0) 
      duplicates += ListWithDuplicates.RemoveAll(z => z.COL1 == col1 && z.COL2 == col2); 
     if (ListWithDuplicates.Count > 0) 
     { 
      foreach (listDataItem item in ListWithDuplicates) 
      { 
       rowcount++; 
       if (ListWithUniques.FindAll(z => z.COL1 == item.COL1 && z.COL2 == item.COL2).Count < 1) 
       { 
        ListWithUniques.Add(ListWithDuplicates.FindAll(z => z.COL1 == item.COL1 && z.COL2 == item.COL2).OrderByDescending(z => z.DATETIME).First()); 
        col1 = item.COL1; 
        col2 = item.COL2; 
        break; 
       } 
      } 
      RecursiveLoopForUnique(ref rowcount, ref duplicates, ref total, col1, col2); 
     } 
     else 
      return; 
    } 

    public class listDataItem 
    { 
     public string COL1 { get; set; } 
     public string COL2 { get; set; } 
     public DateTime DATETIME { get; set; }    

     public listDataItem(string col1, string col2, DateTime datetime) 
     { 
      COL1 = col1; 
      COL2 = col2; 
      DATETIME = datetime; 
     } 
    } 
} 
+0

是否有可能在没有'break'的情况下重新编写它,并且空'return'?这听起来像是在寻求麻烦。 – FrustratedWithFormsDesigner 2010-08-20 21:00:00

+0

@FrustratedWithFormsDesigner - 你是对的。好点子。我添加了额外的if语句,因为我得到了溢出异常。我也不需要将col值传递回递归函数,并且可以在退出foreach后删除它们。我试图超过这个例外的函数搞砸了。没有麻烦;-) – craigpj 2010-08-24 09:39:56

回答

2

这样如何:

Dictionary<string, item> destDict = new Dictionary<string, item>(); 

foreach (item curr in items) 
{ 
    string key = curr.col1 + curr.col2; 
    if (!destDict.Keys.Contains(key)) 
    { 
     destDict.Add(key, curr); 
    } 
    else 
    { 
     if (destDict[key].date < curr.date) 
     { 
      destDict[key].date = curr.date; 
     } 
    } 
} 

我测试该含1000每个的两个独特的COL1/COL2对列表上。工作得很好,比LINQ groupby/select更快。

+0

感谢所有帮助过这个人的人。 @t_scho提供的这种方法效果很好,而且速度非常快。我通过使用DateTime.CompareTo稍微改变了它,并且当DATETIME晚于destinct Dictionary列表中的当前项目时,将curr对象赋予字典。 – craigpj 2010-08-24 09:21:36

+0

Dictionary destDict = new Dictionary (); (在ListWithDuplicates中的listDataItem curr) 如果(!destDict.Keys.Contains(key)) destDict.Add(key,curr); else if(curr.DATETIME.CompareTo(destDict [key] .DATETIME)> 0) { destDict [key] = curr;其他 重复++; } rowcount ++; } – craigpj 2010-08-24 09:23:32

2

LINQ,yay。

listDataItem latestListDataItem = 
    ListWithDuplicates.Where(item => item.COL1 == yourCol1Param && item.COL2 == yourCol2Param) 
         .Max(item => item.DATETIME); 

MSDN便签..

其中:http://msdn.microsoft.com/en-us/library/bb534803.aspx

最大:http://msdn.microsoft.com/en-us/library/bb347632.aspx

排序依据:http://msdn.microsoft.com/en-us/library/bb534966.aspx

末:http://msdn.microsoft.com/en-us/library/bb358775.aspx

+1

你不需要使用OrderBy(...)。Last(),你可以使用Max(item => item.DateTime)来代替。 – Juliet 2010-08-20 21:09:20

+1

我不得不承认,我完全不明白这是如何解决他的问题的。是什么让你认为他正在寻找C1和C2的某个特定组合的日期,而不是所有这些组合? – mquander 2010-08-20 21:09:46

+0

@mquander:我认为这是海报想要从他在帖子中提到的一个东西中得到的东西。他说:“只包含使用这两个字符串作为关键字的最后一个唯一项目”,如果您使用这两个字符串作为关键字,您将获得一组项目,这些项目只有它们的日期唯一,然后将其过滤到最近的日期而你只有一件物品。 – 2010-08-23 12:32:22

0

我不是苏关于语法,但它应该接近。

from d in DupsList 
group d.DATETIME on d.col1, d.col2 in grp 
select new listDataItem (grp.Key.col1, grp.Key.col2, grp.Max()}; 
0

好吧,如果你有超过几千个唯一对C1,C2的越多,那么你会遇到这样的,因为你的每一个独特的群体递归一次。

有很多方法可以解决这个问题;一个会更清晰更快的结果是按C1和C2对列表进行排序,然后精确地查找一次,以找出每个组中的最近日期。如果你不执着于自己的重新实现它,最好的办法是这样的:

ListWithUniques = ListWithDuplicates 
    .GroupBy(x => new { COL1, COL2 }) 
    .Select(g => g.OrderByDescending(x => x.DATETIME).First()) 
0
SELECT col1, col2, MAX(datetime) FROM table GROUP BY col1, col2 

在LINQ:

var query = from row in table 
      group row into g 
      select new 
      { 
       Col1 = g.Key.Col1, 
       Col2 = g.Key.Col2, 
       Date = g.Max(b => b.Date) 
      }; 

而且在可能更有用的形式:

var dict = query.ToDictionary(a => new { a.Col1, a.Col2 }, a => a.Date); 

然后你可以这样引用它:

DateTime specificMaxDate = dict[new { Col1 = 2, Col2 = 3 }]; 
+0

感谢伊恩对你的时间和建议。非常感激。 – craigpj 2010-08-24 09:31:30