locked
Find Item in Dictionary RRS feed

  • Question

  • Hello,

    I have the following list of dictionaries:

    List<Dictionary<String, String>> items;

    What is the best way to find a List row where the dictionary contains and item that:

    item.Key == key && item.Value == value

    I need to get that List row if exists. I need to know if exists. I tried two options:

    rows.FirstOrDefault(x => x.Value.ContainsKey(from) && x.Value.ContainsValue(value));
    
    rows.FirstOrDefault(x => x.Value.Any(y => y.Key == from && y.Value == value));

    I am not sure which is the the fastest ...

    And in case there isn't any I get a KeyValuePair<String, Dictionary<String, String>> with no values instead of null ...

    Thank You,

    Miguel

    Tuesday, November 20, 2012 6:34 PM

Answers

  • This is reflecting a fundamental problem in your design.  Dictionaries are one way.  You have a key, you get the value.  They are not designed for searching on values.

    As an example, use any X/Y dictionary.  The first half is translations from X to Y, and the second half is translations from Y to X.  It's basically the exact same information just duplicated and sorted differently.  You'll need to do the same thing.

    If you have "Hello" in English and you want to find the translation in "pt" then you'll need to find the "En to pt dictionary", which should be a dictionary that has a key of "Hello" and a value of "Olá".  There should also be a dictionary that is "pt" to "en" that has "Olá" as a key and "Hello" as the value.  You can then have some sort of structure that has a collection of all of those dictionaries and can give you the appropriate one when provided with a "from" and "to" language.

    Here is a rough sketch:

    public class Translator
    {
        private Dictionary<string, string> translations;
    
        public Translator(string from, string to, Dictionary<string, string> translations)
        {
            From = from;
            To = to;
            this.translations = translations;
        }
    
        public string From { get; private set; }
        public string To { get; private set; }
    
        public string Translate(string word)
        {
            return translations[word];
        }
    }
    
    public class MyClass  //TODO give better name
    {
        private Dictionary<Tuple<string, string>, Translator> translators;
    
        public MyClass()
        {
            //not sure where these should come from; possibly based on reading in input files.
            //might need to have the file path(s) as an input to this constructor
            translators.Add(Tuple.Create("en", "pt"), createEnToPtTranslator());
            translators.Add(Tuple.Create("pt", "en"), createPtToEnTranslator());
            //TODO add others
        }
    
        private Translator createPtToEnTranslator()
        {
            throw new NotImplementedException();
        }
    
        private Translator createEnToPtTranslator()
        {
            throw new NotImplementedException();
        }
    
        public Translator GetTranslator(string from, string to)
        {
            return translators[Tuple.Create(from, to)];
        }
    }

    • Edited by servy42 Tuesday, November 20, 2012 7:07 PM
    • Marked as answer by MDMoura Tuesday, November 20, 2012 7:37 PM
    Tuesday, November 20, 2012 7:01 PM

All replies

  • string keyToFind = "key", valueToFind = "value";
    var pair = items.FirstOrDefault(dictionary => dictionary.ContainsKey(keyToFind) 
        && dictionary[keyToFind] == valueToFind);

    Your first example won't work because it's not checking if their is the correct pair, you're just checking if there is a key with such and such a key and a value with such and such a value, not if there is a single pair with such and such a key and such and such a value.

    As for the second, you're iterating through every single pair.  That defeats the purpose of having all of the information stored in a dictionary in the first place.  The advantage of a dictionary is that searching for a key, or getting the value associated with a key is a fast operation.

    Tuesday, November 20, 2012 6:40 PM
  • Your first example won't work because it's not checking if their is the correct pair, you're just checking if there is a key with such and such a key and a value with such and such a value, not if there is a single pair with such and such a key and such and such a value.

    Yes, I noticed that ... The problem is that all dictionaries have the same keys ... I changed this a little bit and I have now:

    Dictionary<String, Dictionary<String, String>> translations;
    
    translations.Add("Hello", new Dictionary<String, String> { { "en", "Hello" }, { "pt", "Olá" } })
    
    translations.Add("Source", new Dictionary<String, String> { { "en", "Long Text" }, { "pt", "Texto Longo" } })

    Basically, I am getting items by KEY == Hello, or KEY == Source.

    But if I provide KEY = EN and Value = "Long Text" then, since the main dictionary don't have such a key, I would like to find which one has that match.

    Do you understand what I am trying to do?

    Thank You,

    Miguel

    Tuesday, November 20, 2012 6:52 PM
  • This is reflecting a fundamental problem in your design.  Dictionaries are one way.  You have a key, you get the value.  They are not designed for searching on values.

    As an example, use any X/Y dictionary.  The first half is translations from X to Y, and the second half is translations from Y to X.  It's basically the exact same information just duplicated and sorted differently.  You'll need to do the same thing.

    If you have "Hello" in English and you want to find the translation in "pt" then you'll need to find the "En to pt dictionary", which should be a dictionary that has a key of "Hello" and a value of "Olá".  There should also be a dictionary that is "pt" to "en" that has "Olá" as a key and "Hello" as the value.  You can then have some sort of structure that has a collection of all of those dictionaries and can give you the appropriate one when provided with a "from" and "to" language.

    Here is a rough sketch:

    public class Translator
    {
        private Dictionary<string, string> translations;
    
        public Translator(string from, string to, Dictionary<string, string> translations)
        {
            From = from;
            To = to;
            this.translations = translations;
        }
    
        public string From { get; private set; }
        public string To { get; private set; }
    
        public string Translate(string word)
        {
            return translations[word];
        }
    }
    
    public class MyClass  //TODO give better name
    {
        private Dictionary<Tuple<string, string>, Translator> translators;
    
        public MyClass()
        {
            //not sure where these should come from; possibly based on reading in input files.
            //might need to have the file path(s) as an input to this constructor
            translators.Add(Tuple.Create("en", "pt"), createEnToPtTranslator());
            translators.Add(Tuple.Create("pt", "en"), createPtToEnTranslator());
            //TODO add others
        }
    
        private Translator createPtToEnTranslator()
        {
            throw new NotImplementedException();
        }
    
        private Translator createEnToPtTranslator()
        {
            throw new NotImplementedException();
        }
    
        public Translator GetTranslator(string from, string to)
        {
            return translators[Tuple.Create(from, to)];
        }
    }

    • Edited by servy42 Tuesday, November 20, 2012 7:07 PM
    • Marked as answer by MDMoura Tuesday, November 20, 2012 7:37 PM
    Tuesday, November 20, 2012 7:01 PM
  • Now I understand ...

    But if I have 4 cultures I would get 12 dictionaries ... No?

    Of course, I could fill all dictionaries when adding a value ...

    Tuesday, November 20, 2012 7:05 PM
  • But if I have 4 cultures I would get 12 dictionaries ... No?

    That is correct.  The end result is that each translation of each word will be in N dictionaries, where N is the number of languages that you support; that is the level of redundancy that is added.

    The advantage of this is that you can translate *any* word between *any* two supported languages in O(1) time.  Using your approach you'd use a little over 1/Nth the memory (where N is the number of languages supported) but since you're looking words up by values in the dictionary rather than the key, translating a word will, at best, take O(N) time (where N is the number of words that could be translated by any of the language translations).

    What's more important for you, memory or speed?  If you have enough memory, creating all of the additional dictionaries will result in much faster translations.

    Tuesday, November 20, 2012 7:21 PM
  • Thank you for the help,

    Miguel

    Tuesday, November 20, 2012 7:38 PM