When working with collections in C#, dictionaries provide an efficient way to store and retrieve data using keys. However, one common issue is case sensitivity in dictionary lookups. By default, dictionaries in C# use case-sensitive key comparisons, which can lead to failed lookups when keys are entered in a different case.
In this article, we'll explore how to solve this problem by using StringComparer.OrdinalIgnoreCase
, making dictionary lookups case-insensitive.
Why Use a Dictionary Instead of a List?
Initially, we might store countries in a List<Country>
, but searching through a list has a linear time complexity of O(n). As the dataset grows, lookups become slower.
A dictionary (Dictionary<TKey, TValue>
) improves lookup efficiency by providing O(1) time complexity, meaning searches remain fast even with large datasets.
Example Scenario: Storing and Looking Up Countries by Code
Let's assume we have a Country
class that stores country information:
public class Country
{
public string Code { get; }
public string Name { get; }
public Country(string code, string name)
{
Code = code;
Name = name;
}
}
Now, we need a way to store and retrieve countries by their country code efficiently.
Step 1: Using a Dictionary for Quick Lookups
Instead of using a List<Country>
, we can store the data in a dictionary for fast lookups.
public class AppData
{
public Dictionary<string, Country> AllCountriesByKey { get; private set; }
public void Initialize()
{
var allCountries = new List<Country>
{
new Country("AUS", "Australia"),
new Country("USA", "United States"),
new Country("CAN", "Canada")
};
// Convert List to Dictionary
AllCountriesByKey = allCountries.ToDictionary(country => country.Code);
}
}
How It Works:
- We create a list of
Country
objects. - We use
ToDictionary
to convert the list into a dictionary. - The country code (
Code
) is used as the key.
This allows us to look up a country in O(1) time instead of scanning a list.
Step 2: Implementing a Lookup Method
Now, let's implement a method to retrieve a country by its code.
public Country GetCountryWithCode(string code)
{
AllCountriesByKey.TryGetValue(code, out var country);
return country; // Returns null if the key is not found
}
Why Use TryGetValue
Instead of Square Brackets?
Using square brackets (AllCountriesByKey[code]
) would throw an exception if the key does not exist. Instead, TryGetValue
:
✔ Prevents exceptions
✔ Returns null
if the key is missing
✔ Runs in O(1) time complexity
Step 3: Handling Case Sensitivity
Right now, our dictionary is case-sensitive. If a user enters "aus"
instead of "AUS"
, the lookup fails because "aus"
is not the same as "AUS"
.
To fix this, we must make the dictionary case-insensitive when it is created:
public void Initialize()
{
var allCountries = new List<Country>
{
new Country("AUS", "Australia"),
new Country("USA", "United States"),
new Country("CAN", "Canada")
};
// Convert list to dictionary with case-insensitive keys
AllCountriesByKey = allCountries.ToDictionary(
country => country.Code,
StringComparer.OrdinalIgnoreCase // Enables case-insensitive lookups
);
}
How Does This Fix Case Sensitivity?
- The second argument to
ToDictionary
isStringComparer.OrdinalIgnoreCase
, which:-
Ignores case differences (
"AUS"
=="aus"
=="Aus"
) - Optimizes lookups internally
-
Ignores case differences (
Now, our lookups will work regardless of case.
Step 4: Testing Case-Insensitive Lookups
Now, let's test our updated method:
static void Main()
{
var appData = new AppData();
appData.Initialize();
Console.WriteLine(appData.GetCountryWithCode("AUS")?.Name ?? "Not Found"); // Australia
Console.WriteLine(appData.GetCountryWithCode("aus")?.Name ?? "Not Found"); // Australia
Console.WriteLine(appData.GetCountryWithCode("Usa")?.Name ?? "Not Found"); // United States
Console.WriteLine(appData.GetCountryWithCode("xyz")?.Name ?? "Not Found"); // Not Found
}
Expected Output
Australia
Australia
United States
Not Found
✔ Now, aus
, AUS
, and Aus
all return Australia.
✔ The lookup remains O(1) in performance.
✔ If a country code is not found, it safely returns "Not Found"
.
Bonus: Trimming Whitespace
What if a user enters " AUS "
with spaces? We can trim the input before lookup:
public Country GetCountryWithCode(string code)
{
AllCountriesByKey.TryGetValue(code.Trim(), out var country);
return country;
}
✔ Handles extra spaces automatically
✔ Ensures cleaner input before searching
Conclusion
By switching from List<T>.Find()
to a dictionary with TryGetValue
, we:
- Improved lookup efficiency from O(n) to O(1).
-
Handled case-insensitive keys using
StringComparer.OrdinalIgnoreCase
. - Made our method safer by avoiding exceptions.
- Added whitespace trimming for a better user experience.
This approach ensures that country lookups are fast, reliable, and user-friendly! 🚀
Top comments (0)