Как объединить несколько List

Есть 1000 файлов с email-ами. Нужны посчитать email;количество повторений суммарно во всех файлах. Так как строк очень много, не могу все держать в памяти. Приходится держать глобальный список var all = new List<DB>(), в котором я и буду хранить данные после обработки каждого файла.

public class DB
    {
        public string Email { get; set; }
        public int Total { get; set; }
    }

Вопрос. Как объединить две коллекции List<DB>, чтобы поля Email и Total "суммировались"?


Ответы (1 шт):

Автор решения: Alexander Petrov

Стандартная же задача.

Вот так проходим по всем файлам. Файл целиком в память не грузим, берём по строке.

var dict = new Dictionary<string, int>();

foreach (var file in Directory.EnumerateFiles(path))
{
    foreach (var line in File.ReadLines(file))
    {
        if (dict.ContainsKey(line))
            dict[line]++;
        else
            dict.Add(line, 1);
    }
}

Подразумевается, что каждый email находится на отдельной строке. В реальном коде добавить Trim() и т. п.


Полагаю, есть смысл при создании словаря задать ему большую ёмкость изначально. И нужно задать правильный компаратор.

var dict = new Dictionary<string, int>(1_000_000, StringComparer.OrdinalIgnoreCase);

Ответ уже принят, а я надеялся на развитие дискуссии. Ведь если из 500 млн. строк большинство являются уникальными и е-мейлы длинные, то суммарно может понадобиться несколько десятков гигабайт оперативки.

Соответственно я хотел предложить следующий код

foreach (var file in Directory.EnumerateFiles(path))
{
    foreach (var line in File.ReadLines(file))
    {
        if (dict.ContainsKey(line))
            dict[line]++;
        else
            dict.Add(line, 0);
    }

    AddToStore(dict);
    dict.Clear();
}

Здесь данные из словаря сбрасываются в некое внешнее хранилище (можно sqlite прикрутить) после каждого файла. После чего словарь очищается.

Другой вариант, сбрасывать данные в случае достижения определённого размера словаря. Этот вариант лучше тем, что если дубликатов много, то вызов AddToStore произойдёт меньшее количество раз.

foreach (var file in Directory.EnumerateFiles(path))
{
    foreach (var line in File.ReadLines(file))
    {
        if (dict.ContainsKey(line))
        {
            dict[line]++;
        }
        else
        {
            if (dict.Count >= 1_000_000)
            {
                AddToStore(dict);
                dict.Clear();
            }
            dict.Add(line, 0);
        }
    }
}

Сигнатура метода:

void AddToStore(IDictionary<string, int> dict)
{
    // тут добавляем данные из словаря в некое хранилище
}
→ Ссылка