Introduction
I present here two extension methods that allow me to select an item from a Dictionary
even if I have only the first part of the key, rather than the whole key.
Background
A few times in the past year, I wanted to select an entry in a Dictionary<string,T>
based on only the first part of the key. In early attempts, I added each portion separately; for example if I had a key of "YES
", I would also add "Y
" and "YE
". This became unwieldy as the keys became longer and the benefits of hashing didn't seem as crucial. However, I didn't want to depart from using a Dictionary
entirely.
Adding the use of A String.StartsWith that uses a StringComparer[^] to a Dictionary
was simple.
The Code
BestMatch
will attempt the usual ContainsKey
first and only resort to iterating the keys and using StartsWith
if the full string
isn't found. If it does have to iterate, then it can't stop as soon as it finds a match, it must continue in case it finds a second match.
public static T
BestMatch<T>
(
this System.Collections.Generic.Dictionary<string,T> Source
,
string String
)
{
T result = default(T) ;
if ( Source.ContainsKey ( String ) )
{
result = Source [ String ] ;
}
else
{
bool found = false ;
foreach ( string s in Source.Keys )
{
if ( s.StartsWith ( String , Source.Comparer ) )
{
if ( found )
{
throw ( new System.ArgumentException ( "Ambiguous name" , String ) ) ;
}
result = Source [ s ] ;
found = true ;
}
}
if ( !found )
{
throw ( new System.ArgumentException ( "Unrecognized name" , String ) ) ;
}
}
return ( result ) ;
}
The second method works similarly to TryParse
in that it returns a boolean and the value (or the default for the type) is passed back via an out
parameter. If you want to change the name to TryBestMatch
or HasBestMatch
or something, go ahead.
public static bool
BestMatch<T>
(
this System.Collections.Generic.Dictionary<string,T> Source
,
string String
,
out T Value
)
{
bool result = true ;
Value = default(T) ;
try
{
Value = BestMatch ( Source , String ) ;
}
catch
{
result = false ;
}
return ( result ) ;
}