Is it possible to do a partial string match on a Dictionary string key?

独自空忆成欢 提交于 2019-12-21 03:32:42

问题


I have a Dictionary<string, List<int>> in my code which I am using in the following manner:

Key           Values  
2011-07-15    1, 2, 3
2011-07-20    4, 5, 6
2010-02-11    7, 8, 9

My code needs to be able to query for all values matching a particular substring in the key. For example, if I had the substring 2011-07 it should return values {1, 2, 3, 4, 5, 6}. A substring of 11 should return all IDs from 1-9.

Can anyone recommend a concise way to achieve this? Or provide a better data structure for retrieving this information?


回答1:


I would do an extension method :

public static class DictionaryExt
{
    public static IEnumerable<T> PartialMatch<T>(this Dictionary<string, T> dictionary, string partialKey)
    {
        // This, or use a RegEx or whatever.
        IEnumerable<string> fullMatchingKeys = 
            dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey));

        List<T> returnedValues = new List<T>();

        foreach (string currentKey in fullMatchingKeys)
        {
            returnedValues.Add(dictionary[currentKey]);
        }

        return returnedValues;
    }
}

The "cost" of adding values to the dictionary wouldn't change, but the cost of retrieval would be higher, but only when you know you're going with a partial match.

Btw, I'm sure you could transform this in a single Lambda expression, but the concept remains the same.

Edit: In your example, this method would return 2 lists of values, but you can change it to merge the lists. Here is the extension method you could do :

public static IEnumerable<T> PartialMatch<T>(
    this Dictionary<string, IEnumerable<T>> dictionary,
    string partialKey)
{
    // This, or use a RegEx or whatever.
    IEnumerable<string> fullMatchingKeys = 
        dictionary.Keys.Where(currentKey => currentKey.Contains(partialKey));

    List<T> returnedValues = new List<T>();

    foreach (string currentKey in fullMatchingKeys)
    {
        returnedValues.AddRange(dictionary[currentKey]);
    }

    return returnedValues;
}

Edit 2: Come to think of it, you could also make it more generic. With the next extension method, it would work on any dictionary, as long as you provide a comparer that check what you mean by "partial match" :

public static IEnumerable<TValue> PartialMatch<TKey, TValue>(
    this Dictionary<TKey, IEnumerable<TValue>> dictionary,
    TKey partialKey,
    Func<TKey, TKey, bool> comparer)
{
    // This, or use a RegEx or whatever.
    IEnumerable<TKey> fullMatchingKeys = 
        dictionary.Keys.Where(currentKey => comparer(partialKey, currentKey));

    List<TValue> returnedValues = new List<TValue>();

    foreach (TKey currentKey in fullMatchingKeys)
    {
        returnedValues.AddRange(dictionary[currentKey]);
    }

    return returnedValues;
}



回答2:


You are looking for concise answers. Without fancy indexing at a low-level for text (of which I don't know of any specialized .Net classes), I think dictionary is still your best bet. Query with something like:

myDictionary.Where(kvp => kvp.Key.Contains("11")).SelectMany(kvp => kvp.Value);

You have to search through all keys for a generalized substring anyway without some pretty cool magic (not provided by .Net), so LINQ shouldn't hurt you much here.




回答3:


If Dictionary uses internally hashes, you are out of luck, as similar strings yield dissimilar hashes. I just implemented solution to this requirement over the weekend in C, an interview test/homework. I used a sorted array as the underlying structure - expensive inserts, but fast lookups (using binary search). To find all entries with key starting with a prefix, I would find the 1st, then just go next, next... For general substring, i.e. not only prefix, my solution would not work. At this moment I do not know what to suggest for the "general substring" search.




回答4:


You could have three dictionaries. Year, Month, Day.

Note that when you add items to three dictionaries, you are NOT duplicating the items.

When you pull items out using two keys, you could use the LINQ Extension method Intersect() to get the items that match both keys (Use Intersect on the two result sets).

Caveat, doing it this way would not result in the fastest executing code.




回答5:


A concise way would be to use Multivalue Map.

For example:

Dictionary<string, Dictionary<string, List<int>>

why dont you store the 2011-07 as a key and 15 for the inner dictionary key and 1,2,3 as values.

map["2011-07"]["15"]= {1,2,3};

if you want just 2011-07 you can get everything within the other dictionary by traversal.

map["2011-07"] // would return u 1,2,3,4,5,6

and if you want to go to a specific day, 2011-07-15, this would return u only 1,2,3

foreach(var element in map["2011-07"]){

     var values = element.values; // and you can append them to a list.

}

if you will need year/month/day, you will need multilevel dictionaries. or you can use a Tree as well.



来源:https://stackoverflow.com/questions/7816398/is-it-possible-to-do-a-partial-string-match-on-a-dictionary-string-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!