Как отсортировать и «обрезать» список словарей в C # - PullRequest
3 голосов
/ 24 октября 2011

Я хочу использовать только первые N элементов списка и удалить остальные.Я думал, что продублировал и отсортировал совпадения как _listTopN , но когда я очищаю совпадения , он также очищает _listTopN

КорочеКак я могу получить не более 3 элементов списка в неупорядоченном списке словаря и вернуть их в тот же словарь?

Dictionary<int, float> matches;

...

if (matches.Count > Settings.requiredMatch)
{
   var _listTopN = matches.OrderByDescending(s => s.Value).Take(Settings.requiredMatch);

   matches.Clear();

   foreach (var p in _listTopN)
      matches.Add(p.Key, p.Value);

}

Ответы [ 6 ]

5 голосов
/ 24 октября 2011

Linq запросы оцениваются лениво. Ваш оператор, которому вы назначаете _listTopN, не выполняет никакой работы, он только готовит запрос; этот запрос будет выполнен только тогда, когда вы начнете перечислять результаты. Поскольку вы очищаете matches перед тем, как начать перечисление запроса, в источнике нет ничего для перечисления ...

Если вы хотите, чтобы запрос оценивался с нетерпением, добавьте ToList в конце:

var _listTopN = matches.OrderByDescending(s => s.Value)
                       .Take(Settings.requiredMatch)
                       .ToList();

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

matches = matches.OrderBy(s => s.Value)
                 .Take(Settings.requiredMatch)
                 .ToDictionary(s => s.Key, s => s.Value);
3 голосов
/ 24 октября 2011

Вы можете присвоить результат matches с помощью ToDictionary:

if (matches.Count > Settings.requiredMatch)
{
    matches = matches
        .OrderByDescending(s => s.Value)
        .Take(Settings.requiredMatch)
        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
3 голосов
/ 24 октября 2011

Принудительно оценивать его раньше:

var _listTopN = matches.OrderByDescending(s => s.Value)
    .Take(Settings.requiredMatch).ToList();

LINQ отложил выполнение (как часть композиции);в большинстве случаев ничто на самом деле не оценивается , пока вы не foreach с данными.ToList() делает это раньше, то есть до того, как вы очистите словарь.

2 голосов
/ 24 октября 2011

Ваше выражение LINQ _listTopN лениво оценивается, что означает, что оно не оценивает свой результат до вашего foreach утверждения.Но на этом этапе вы уже очистили matches источник, поэтому вы также ничего не получите в _listTopN.Вы можете заставить методы LINQ оценивать их результаты, например, вызывая ToArray.

 var _listTopN = matches.OrderByDescending(s => s.Value).Take(Settings.requiredMatch)
                          .ToArray(); 

См. Этот оператор в MSDN

Этот метод реализованс помощью отложенного выполнения

2 голосов
/ 24 октября 2011

Это потому, что _listTopN удерживает отложенный запрос, лениво выполняемый при его цикле.

Другими словами, var _listTopN = ... не оценивает запрос, а возвращает рецепт того, какчтобы оценить его при необходимости.

Поскольку вы очищаете исходный источник перед его оценкой, он «изменится», то есть вернет нечто иное, чем вы хотели / ожидали.

Простое исправление состоит в форсировании оценки, поэтому сделайте следующее:

var _listTopN = matches.OrderByDescending(s => s.Value)
    .Take(Settings.requiredMatch).ToArray();
                                 ^--------^  <-- add this

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

1 голос
/ 24 октября 2011
if (matches.Count > Settings.requiredMatch)
  matches = matches.OrderByDescending(s => s.Value)
  .Take(Settings.requiredMatch)
  .ToDictionary(s => s.Key, s => s.Value);

Получает matches в нужное вам состояние просто и понятно.

...