DEV Community

mohamed Tayel
mohamed Tayel

Posted on

1

Optimizing Lookups in C# Dictionaries with Case-Insensitive Keys

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
    );
}
Enter fullscreen mode Exit fullscreen mode

How Does This Fix Case Sensitivity?

  • The second argument to ToDictionary is StringComparer.OrdinalIgnoreCase, which:
    • Ignores case differences ("AUS" == "aus" == "Aus")
    • Optimizes lookups internally

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
}
Enter fullscreen mode Exit fullscreen mode

Expected Output

Australia
Australia
United States
Not Found
Enter fullscreen mode Exit fullscreen mode

✔ 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;
}
Enter fullscreen mode Exit fullscreen mode

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! 🚀

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more