Рекурсивный цикл в List <>, вызывающий стекопоток - PullRequest
0 голосов
/ 21 августа 2010

У меня есть List<> объектов, содержащих две строки и DateTime.Я хочу создать еще один список из тех же объектов, содержащий только последние уникальные элементы, используя две строки в качестве ключей и последнее значение DateTime.В SQL подумайте о следующем:

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

. Это дает уникальный список col1, col2 и даты последнего времени.Итак .. Я пытаюсь сделать это в коде с двумя списками.Один с дубликатами в нем, которые анализируют и извлекают только последние уникальные элементы из него, чтобы заполнить второй список.

Наборы данных, которые у меня есть, огромны, поэтому просто просматривая список дубликатов, затем проверяете, является ли элементв уникальном списке, если он не добавляется, сравнивать даты и т. д. довольно медленно.Поэтому я подумал, что могу рекурсивно просмотреть список дубликатов, найти уникальные элементы, найти их максимальное время и удалить не максимальные, когда я перебираю цикл, делая мой дубликат списка все меньше и меньше, тем самым ускоряя процесс.(Я надеюсь, что вы все еще следуете за мной ..)

Так или иначе.Я написал рекурсивный цикл с двумя списками, но при прохождении цикла я получаю System.StackOverflowException о 3000-й итерации.

Вот мой код.Представьте, что 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;
        }
    }
}

Ответы [ 5 ]

2 голосов
/ 21 августа 2010

Как насчет этого:

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 каждой из 2 уникальных пар col1 / col2.Работал нормально и был быстрее, чем LINQ groupby / select.

2 голосов
/ 21 августа 2010

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

OrderBy: http://msdn.microsoft.com/en-us/library/bb534966.aspx

Последнее: http://msdn.microsoft.com/en-us/library/bb358775.aspx

0 голосов
/ 21 августа 2010
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 голосов
/ 21 августа 2010

Что ж, если у вас есть несколько тысяч уникальных пар C1, C2, то вы столкнетесь с этим, поскольку вы повторяете один раз для каждой уникальной группы.

Есть много способовВы могли бы это исправить;то, что получилось бы намного яснее и быстрее, - это отсортировать список по C1 и C2, а затем спуститься по нему ровно один раз, чтобы найти самую последнюю дату в каждой группе.Если вы не готовы переопределить это самостоятельно, лучший способ это:

ListWithUniques = ListWithDuplicates
    .GroupBy(x => new { COL1, COL2 })
    .Select(g => g.OrderByDescending(x => x.DATETIME).First())
0 голосов
/ 21 августа 2010

Я не уверен насчет синтаксиса, но он должен быть близким.

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()};
...