C# Оптимизация вложенных циклов foreach

Я написал библиотеку, которая работает с большим Excel и отображает записи в объекты. Но в какой-то момент он становится очень медленным из-за трех вложенных циклов foreach. Я видел некоторые решения со словарем, но проблема была немного другой, чем моя.

var house = new PostHouse();
house.FK_STRID = long.Parse(fields[2]);
if (!fields[3].Equals("")){
     house.HouseNumber = long.Parse(fields[3]);
}
foreach (var canton in cantons)
{
    foreach(var city in canton.Cities)
    {
        if (city.Streets == null) 
            city.Streets = new List<PostStreet>();
        foreach(var street in city.Streets)
        {
            if (street.STRID == house.FK_STRID)
            {
                if (street.Houses == null) 
                    street.Houses = new List<PostHouse>();
                street.Houses.Add(house);
            }
        }
    }
}

Чтобы перебрать все в цикле, требуется около 10 минут.

Любая помощь?

заранее спасибо

выполнение цикла: цикл For - лучший вариант, если вы не можете использовать цикл For, вы можете использовать foreach с локальной переменной. (var cantons = кантоны и foreach (var canton в кантонах)

Frenchy 24.11.2018 10:29

Я так понимаю, что проблема не в цикле foreach. Проблема заключается в чтении из файла xls. Вы можете прочитать это как файл csv?

Ole EH Dufour 24.11.2018 10:31

По крайней мере, вы можете извлечь house.FK_STRID в локальную переменную вне циклов. Наверное, это не сильно поможет, но это стоит сделать.

Wai Ha Lee 24.11.2018 10:32

Чтобы проделать эти последние 10 минут, понадобится вся Швейцария. Нет простых уловок для слишком большого количества данных. На самом деле вы можете сделать только одно: сделать это один раз и поместить в хорошо проиндексированную базу данных. Если вы не можете сделать это только один раз, сосредоточьтесь на чтении только измененных данных. Если и этого не получается, убедитесь, что этого никто не ждет. Все спят в 3 часа ночи :)

Hans Passant 24.11.2018 10:47

Откуда взялся cantons?

Gert Arnold 24.11.2018 10:59

Вы использовали профилировщик? Где именно узкое место?

Alexander Petrov 24.11.2018 12:27

Попробуй распараллелить код.

Alexander Petrov 24.11.2018 12:28
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
1 315
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Трудно сказать, потому что мы не знаем, что это за тип cantons и его дочерние элементы и откуда они берутся, но основная проблема здесь в том, что у вас есть структура данных, которая не подходит для этой задачи. У вас есть иерархический список улиц внутри городов внутри кантонов, тогда как вам нужен список улиц проиндексирован по их STRID.

Если вы можете выполнить первоначальную обработку того, как хранятся ваши улицы, вы можете сделать это:

var streetIndex = new Dictionary<string, PostStreet>();
foreach (var canton in cantons)
  foreach (var city in canton.Cities)
    foreach (var street in city.Streets)
    { 
        streetIndex.Add(street.STRID, street);
    }

тогда вы можете мгновенно искать улицу по ее STRID, когда вы перебираете дома за O (1) раз.

foreach (var house in houses)
{
    if (streetIndex.ContainsKey(house.FK_STRID))
    {
       streetIndex[house.FK_STRID].Add(house);   
    }
}

К сожалению, в настоящее время это невозможно, потому что я читаю одну строку файла, а затем собираюсь добавить ее в объект. Итак, предварительная обработка здесь не работает ..

Dominic Järmann 24.11.2018 10:50

@ DominicJärmann Если вы читаете файл по одной строке за раз, то, скорее всего, проблема в скорости, читайте его по частям (или, если он маленький, все сразу)

Ronan Thibaudau 24.11.2018 11:56

Сделайте это в своих конструкторах или автоматически инициализируйте их

Streets = new List<PostStreet>();

...

Houses = new List<PostHouse>()

Тогда Linq

var streets = cantons.SelectMany(x => x.Cities)
                     .SelectMany(x => x.Streets)
                     .Where(x => x.STRID == house.FK_STRI);


foreach (var street in streets)
    street.Houses.Add(house);

Другие вопросы по теме