Сортировка словаря по значению в C#: полный гайд с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему сортировка словаря по значению — это головная боль (и как её вылечить)

Вы когда-нибудь сталкивались с ситуацией: у вас есть Dictionary<string, int> с десятками записей, и вам нужно вывести их в порядке убывания популярности? Например, список самых частых слов в тексте или топ-10 продуктов по продажам. Вроде бы простая задача, но стандартный Dictionary в C# хранит данные в произвольном порядке. На Stack Overflow вопрос "How do you sort a dictionary by value?" набрал тысячи просмотров — и это не случайно.

В этом туториале мы разберём все легальные способы сортировки Dictionary по значению в C#: от простого LINQ-лайфхака до создания собственного упорядоченного словаря. Вы узнаете, какой метод выбрать для максимальной производительности и читаемости кода.

Почему Dictionary не сортируется сам по себе?

Прежде чем нырять в код, давайте разберёмся с природой Dictionary<TKey, TValue>. Этот тип данных — хеш-таблица. Он оптимизирован для быстрого поиска по ключу, а не для хранения элементов в определённом порядке. Когда вы добавляете элемент, он попадает в «корзину» на основе хеша ключа. Никакой сортировки не происходит.

Если вам нужен отсортированный вывод — придётся вмешаться вручную. Хорошая новость: C# предоставляет несколько элегантных решений.

Метод 1: LINQ — самый популярный способ (как в ответе на Stack Overflow)

Самый простой и часто используемый подход — использовать LINQ и метод OrderBy или OrderByDescending. Этот вариант идеален, когда вам нужно один раз отсортировать данные для отображения или экспорта.

using System;
using System.Collections.Generic;
using System.Linq;

var fruits = new Dictionary<string, int>
{
    {"apple", 5},
    {"banana", 3},
    {"orange", 8},
    {"grape", 1}
};

// Сортировка по возрастанию значения
var sortedAsc = fruits.OrderBy(pair => pair.Value);

// Сортировка по убыванию значения
var sortedDesc = fruits.OrderByDescending(pair => pair.Value);

Console.WriteLine("По возрастанию:");
foreach (var item in sortedAsc)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

Console.WriteLine("\nПо убыванию:");
foreach (var item in sortedDesc)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

Результат:

По возрастанию:
grape: 1
banana: 3
apple: 5
orange: 8

По убыванию:
orange: 8
apple: 5
banana: 3
grape: 1

Как это работает?

OrderBy возвращает IOrderedEnumerable<KeyValuePair<string, int>> — последовательность, которая сортируется «лениво» (только при первом переборе). Это не новый словарь, а проекция. Если вам нужно сохранить результат, преобразуйте его обратно в словарь с помощью .ToDictionary().

Важно: ToDictionary создаёт обычный Dictionary, который снова не гарантирует порядок. Если порядок критичен — используйте SortedDictionary или список кортежей.

Метод 2: SortedDictionary — когда порядок нужен постоянно

Если вы планируете многократно обращаться к отсортированным данным, лучше использовать SortedDictionary. Этот класс автоматически поддерживает сортировку по ключу. Но нам-то нужно по значению!

Хитрость: мы не можем заставить SortedDictionary сортироваться по значению напрямую. Но мы можем использовать SortedDictionary с кастомным компаратором или, что проще, создать вспомогательный класс.

using System;
using System.Collections.Generic;
using System.Linq;

var fruits = new Dictionary<string, int>
{
    {"apple", 5},
    {"banana", 3},
    {"orange", 8},
    {"grape", 1}
};

// Создаем SortedDictionary, сортирующий по значению (через LINQ и ToDictionary)
var sortedDict = new SortedDictionary<int, string>();
foreach (var pair in fruits)
{
    // Ключом становится значение, значением — ключ
    sortedDict[pair.Value] = pair.Key;
}

Console.WriteLine("SortedDictionary (ключи = значения исходного словаря):");
foreach (var item in sortedDict)
{
    Console.WriteLine($"{item.Value}: {item.Key}");
}

Минус: Этот подход работает только если значения уникальны. Если есть дубликаты, последний ключ перезапишет предыдущий. Для реальных задач лучше использовать список List<KeyValuePair>.

Метод 3: Сортировка через List — полный контроль

Самый гибкий способ — преобразовать словарь в список пар «ключ-значение» и отсортировать его с помощью List.Sort() или LINQ. Это даёт полный контроль над компаратором и позволяет сортировать по сложным правилам.

using System;
using System.Collections.Generic;
using System.Linq;

var fruits = new Dictionary<string, int>
{
    {"apple", 5},
    {"banana", 3},
    {"orange", 8},
    {"grape", 1}
};

// Преобразуем в список
var list = fruits.ToList();

// Сортировка по значению (по возрастанию)
list.Sort((pair1, pair2) => pair1.Value.CompareTo(pair2.Value));

Console.WriteLine("Сортировка через List (по возрастанию):");
foreach (var item in list)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

// Сортировка по значению (по убыванию)
list.Sort((pair1, pair2) => pair2.Value.CompareTo(pair1.Value));

Console.WriteLine("\nСортировка через List (по убыванию):");
foreach (var item in list)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

Когда это полезно?

Когда нужно отсортировать по нескольким полям. Например, сначала по значению, а при равенстве — по ключу:

list.Sort((pair1, pair2) => 
{
    int result = pair1.Value.CompareTo(pair2.Value);
    if (result == 0)
        result = pair1.Key.CompareTo(pair2.Key);
    return result;
});

Метод 4: Кастомный класс-обёртка — для эстетов

Если вы пишете библиотеку или часто сталкиваетесь с задачей, можно создать класс, который наследует Dictionary и хранит отсортированный список. Но это скорее академический интерес — для продакшена часто хватает LINQ.

public class SortedByValueDictionary<TKey, TValue> where TValue : IComparable<TValue>
{
    private readonly Dictionary<TKey, TValue> _dictionary = new();
    private readonly List<KeyValuePair<TKey, TValue>> _sortedList = new();

    public void Add(TKey key, TValue value)
    {
        _dictionary[key] = value;
        _sortedList.Add(new KeyValuePair<TKey, TValue>(key, value));
        _sortedList.Sort((pair1, pair2) => pair1.Value.CompareTo(pair2                

Блоги

Рекомендаций по книгам