Самый быстрый способ поиска объектов в очень большом списке по строке C#

#c# #linq #search

#c# #linq #Поиск

Вопрос:

например, у меня есть класс, подобный приведенному ниже :

 public class MasterRecord
{
     public int Id { get; set; }
     public string UniqueId{ get; set; }
}

public class DetailRecord
{
     public int Id { get; set; }

     public int MasterRecordId { get; set; }

     public string UniqueId{ get; set; }
}
  

и я также перечисляю 2, которые:

masterList и DetailList

В masterList будет около 300 000 записей, в DetailList — около 7 000 000 записей

Что мне нужно, так это выполнить цикл для каждой записи в главном списке и выполнить поиск записей с одинаковым именем в DetailList.

Вот мой код :

  foreach (var item in MasterList)
 {
    var matchPersons = DetailList.Where(q => q.UniqueId == item .UniqueId).ToList();

    if (matchPersons != null amp;amp; matchPersons.Count() > 0)
    {
        foreach (var foundPerson in matchPersons)
        {
            //Do something with foundPerson
            foundPerson.MasterRecordId = item.Id;
        }
    }
 }
  

Сейчас мой код работает очень медленно, каждый поиск обходится мне в 500 миллисекунд, поэтому при 300 тысячах записей на завершение потребуется 2500 минут : ( .
Есть ли какой-либо другой способ ускорить эту функцию?
Спасибо и простите за мой плохой английский.

Обновленный код для того, чтобы сделать более понятным то, что я хочу сделать.

Комментарии:

1. Ваш список поступает из базы данных?

2. Создание словаря займет время, но такое же значительное при выполнении нескольких поисков. Dictionary<строка, персона> dict1 = masterList .GroupBy(x => x.Name , y => y) .ToDictionary(x => x.Key, y => y.FirstOrDefault()); Или, если у вас есть несколько пользователей с одинаковыми именами Dictionary<строка, список<Персона>> dict2 = masterList .GroupBy(x => x.Name , y => y) .ToDictionary(x => x.Key, y => y.ToList());

Ответ №1:

Использование некоторой хэш-структуры было бы одним из лучших вариантов:

 var detailLookup = DetailList.ToLookup(q => q.Name);
foreach (var person in MasterList)
{
    foreach (var foundPerson in detailLookup[person.Name])
    {
        //Do something with foundPerson                
    }
}
  

Поиск возвращает пустую последовательность, если ключ отсутствует, поэтому вам не нужно его проверять.

Ответ №2:

Вы могли бы использовать объединение по имени.

 var result = masterList.Join(detailedList,m=>m.Name,d=>d.Name,(m,d)=>d);
  

Ответ №3:

Если вам нужно обработать «MasterRecords с их DetailRecords», не используйте обычное соединение, используйте GroupJoin. Это внутренне создаст нечто похожее на таблицу поиска.

Приятно то, что это также будет работать с базами данных, CSV-файлами или любым другим методом, который вы используете для получения ваших записей. Вам не нужно сначала преобразовывать их в списки.

 // Your input sequences, if desired: use IQueryable
IEnumerable<MasterRecord> masterRecords = ...
IEnumerable<DetailRecord> detailRecords = ...
// Note: query not executed yet!

// GroupJoin these two sequences
var masterRecordsWithTheirDetailRecords = masterRecord.GroupJoin(detailRecords,
    masterRecord => masterRecord.Id,             // from masterRecord take the primary key
    detailRecord => detailRecord.MasterRecordId  // from detailRecord take the foreign key

    // ResultSelector: from every MasterRecord with its matching DetailRecords select
    (masterRecord, detailRecords) => new
    {
        // select the properties you plan to use:
        Id = masterRecord.Id,
        UniqueId = maserRecord.UniqueId,
        ...

        DetailRecords = detailRecords.Select(detailRecord => new
        {
            // again: select only the properties you plan to use
            Id = detailRecord.Id,
            ...

            // not needed, you know the value:
            // MasterRecordId = detailRecord.MasterRecordId,
        }),
        // Note: this is still an IEnumerable!            
     });
  

Использование:

 foreach(var masterRecord in masterRecordsWithTheirDetailRecords)
{
    ... // process the master record with its detail records
}
  

Самое приятное, что вам нужно обработать только некоторые из мастер-записей
(например, после 1000-го вы решаете, что нашли то, что искали),
или, если у вас есть несколько основных записей, из которых вам не нужны все подробные записи, обрабатывается не больше записей, чем необходимо. Linq позаботится об этом

Комментарии:

1. Спасибо за вашу помощь, но в моем случае у меня было 2 таблицы, которые были импортированы уже из другой базы данных. Но по какой-то причине в то время у меня не было внешнего ключа в подробной таблице. Итак, мне нужно запустить эту функцию для сопоставления основной и подробной таблицы вместе с помощью поля уникального идентификатора. Поэтому не уверен, поможет ли ваше решение.