Sortowanie nazw (stringów) w C#

Czego szuka rekruter:
StringComparer, porównanie kulturowe,Array.Sortvs LINQ, stabilność sortowania
Poziom: Junior
Zadanie
Posortuj tablicę nazw (stringów) alfabetycznie — rosnąco i malejąco.
Czego szuka rekruter
Sortowanie stringów to pozornie proste zadanie z ukrytymi pułapkami. Rekruter sprawdza:
- Czy kandydat wie, że domyślne porównanie stringów jest
Ordinal— “Z” < “a” (wielkie litery mają niższy kod ASCII) - Czy zna
StringCompareri różnicę międzyOrdinal,OrdinalIgnoreCase,CurrentCulture - Czy rozumie, że
Array.Sortmodyfikuje oryginalną tablicę, a LINQOrderBytworzy nową nullw tablicy —StringComparerobsługuje to inaczej niż porównanie operatorem
Pułapka — sortowanie wielkość-liter-świadome
var names = new[] { "anna", "Zofia", "marek", "Beata" };
Array.Sort(names); // Ordinal domyślnie
// Wynik: "Beata", "Zofia", "anna", "marek"
// Bo 'B' (66) < 'Z' (90) < 'a' (97) < 'm' (109)
// Wielkie litery wchodzą przed małymi!
✅ Poprawne sortowanie — kulturowo-świadome, ignorując wielkość
static string[] SortNames(string[] names, bool ascending = true)
{
if (names == null) throw new ArgumentNullException(nameof(names));
var comparer = StringComparer.CurrentCultureIgnoreCase;
return ascending
? names.OrderBy(n => n, comparer).ToArray()
: names.OrderByDescending(n => n, comparer).ToArray();
}
var names = new[] { "anna", "Zofia", "marek", "Beata", null };
var sorted = SortNames(names);
// Wynik: null, "anna", "Beata", "marek", "Zofia"
Kiedy który StringComparer?
// Ordinal — szybkie, bajt po bajcie, case-sensitive
// Używaj dla: kluczy słownika, identyfikatorów, porównań binarne-równe
StringComparer.Ordinal
// OrdinalIgnoreCase — szybkie, bajt po bajcie, case-insensitive
// Używaj dla: nazw plików, HTTP headerów, kluczy config
StringComparer.OrdinalIgnoreCase
// CurrentCulture — kulturowo-świadome (ą, ę, ó...), case-sensitive
// Używaj dla: danych wyświetlanych użytkownikowi w jego języku
StringComparer.CurrentCulture
// CurrentCultureIgnoreCase — kulturowo-świadome, case-insensitive
// Używaj dla: sortowania nazw dla użytkownika
StringComparer.CurrentCultureIgnoreCase
// InvariantCulture — kultura neutralna, deterministyczna między środowiskami
// Używaj dla: danych do zapisu/odczytu z pliku, API
StringComparer.InvariantCulture
Modyfikacja in-place vs nowa tablica
var names = new[] { "marek", "anna", "zofia" };
// Array.Sort — modyfikuje oryginalną tablicę
Array.Sort(names, StringComparer.CurrentCultureIgnoreCase);
// names jest teraz: ["anna", "marek", "zofia"]
// LINQ OrderBy — nie modyfikuje, zwraca nową sekwencję
var sorted = names.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase).ToArray();
// names niezmienione, sorted to nowa tablica
Malejąco z Array.Sort:
Array.Sort(names, StringComparer.CurrentCultureIgnoreCase);
Array.Reverse(names); // Odwróć po sortowaniu rosnącym
Stabilność sortowania — dlaczego to ma znaczenie
Sortowanie jest stabilne, jeśli elementy uznane za “równe” zachowują swoją oryginalną względną kolejność po posortowaniu. To niepozorna właściwość, która staje się istotna, gdy sortujesz po jednym kluczu, ale kolejność w ramach tego samego klucza wciąż ma znaczenie — np. lista osób posortowana najpierw po dacie rejestracji, a potem (stabilnie) po imieniu, tak by przy identycznych imionach zachować kolejność rejestracji.
var osoby = new[]
{
(Imie: "Anna", DataRejestracji: 3),
(Imie: "Anna", DataRejestracji: 1),
(Imie: "Marek", DataRejestracji: 2),
};
// LINQ OrderBy jest gwarantowane stabilne — przy remisie zachowuje kolejność wejściową
var poImieniu = osoby.OrderBy(o => o.Imie, StringComparer.CurrentCultureIgnoreCase).ToArray();
// obie "Anna" zostają w kolejności: DataRejestracji 3, potem 1 (tak jak w wejściu)Kluczowa różnica: LINQ OrderBy/OrderByDescending jest gwarantowane stabilne w dokumentacji .NET — możesz na tym polegać. Array.Sort nie gwarantuje stabilności — wewnętrznie używa introspektywnego sortowania (hybryda quicksort/heapsort/insertion sort), które przy remisach może przestawić kolejność elementów w sposób niezdefiniowany. Jeśli stabilność ma znaczenie dla Twojego przypadku (a przy sortowaniu ludzkich danych — nazwisk, dat — często ma), wybierz LINQ, nie Array.Sort.
Testy jednostkowe
[Fact]
public void SortNames_Ascending_IgnoresCase()
{
var result = SortNames(new[] { "Zofia", "anna", "Beata", "marek" });
Assert.Equal(new[] { "anna", "Beata", "marek", "Zofia" }, result);
}
[Fact]
public void SortNames_Descending_ReturnsReverseOrder()
{
var result = SortNames(new[] { "Zofia", "anna", "Beata" }, ascending: false);
Assert.Equal(new[] { "Zofia", "Beata", "anna" }, result);
}
[Fact]
public void SortNames_WithNulls_NullsFirst()
{
var result = SortNames(new[] { "marek", null, "anna" });
Assert.Null(result[0]);
}
[Fact]
public void SortNames_EmptyArray_ReturnsEmpty()
{
Assert.Empty(SortNames(Array.Empty<string>()));
}
Co powiedzieć na rozmowie
“Domyślne sortowanie stringów w C# to Ordinal — porównanie bajt po bajcie, gdzie wielkie litery (‘A’=65) są przed małymi (‘a’=97). Dla sortowania nazw użytkowników chcę CurrentCultureIgnoreCase żeby ‘anna’ i ‘Anna’ były traktowane jako równe i żeby polskie znaki (ą, ę) były posortowane poprawnie. LINQ OrderBy jest bezpieczniejszy bo nie mutuje oryginalnej tablicy i, w przeciwieństwie do Array.Sort, jest gwarantowane stabilne.”
Podsumowanie
Kluczowe decyzje: który StringComparer (zależy od kontekstu), Array.Sort (in-place, niestabilne) vs LINQ (nowa sekwencja, gwarantowanie stabilne). Dla danych użytkownika zawsze CurrentCulture lub CurrentCultureIgnoreCase.
Powiązane: to zadanie dotyka porównywania i sortowania — zobacz algorytm sortowania bąbelkowego i alternatywę z LINQ. Teoria stringów: łańcuchy znaków.
🚀 Co dalej?
Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.
- 🗺️ Pobierz darmową roadmapę Junior .NET Developer — 12 kroków od podstaw C# do pierwszej pracy: dev-hobby.pl
- 🎬 Subskrybuj kanał YouTube — nowe filmy co tydzień.
2 comments
Dodaj komentarz
Musisz się zalogować, aby móc dodać komentarz.
Zamień wiedzę w umiejętności
Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.
Pobieram roadmapę →
Twój artykuł jest jak mała skarbnica wiedzy. Dzięki za podzielenie się z nam
Talentem bije każdy Twój wpis i post, twórcze myślenie nie sprawia Ci problemu, to się nazywa intelektualny most, on sprawia wielką radość każdemu odwiedzającemu.