4

I have this structure:

static Dictionary<int, Dictionary<int, string>> tasks = 
    new Dictionary<int, Dictionary<int, string>>();

it looks like that

[1]([8] => "str1")
[3]([8] => "str2")
[2]([6] => "str3")
[5]([6] => "str4")

I want to get from this list all of the [8] strings, meaning str1 + str2
The method should look like the following:

static List<string> getTasksByNum(int num){

}

How do I access it?

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
SexyMF
  • 10,657
  • 33
  • 102
  • 206
  • 1
    loop all, but it is too heavy – SexyMF Sep 20 '11 at 14:09
  • 2
    You should return an IEnumerable rather than a List – Joel Coehoorn Sep 20 '11 at 14:10
  • A Dictionary inside a Dictionary? – Ethan Sep 20 '11 at 14:10
  • Define "heavy?" Whatever method you use will use a loop... – Dan Puzey Sep 20 '11 at 14:11
  • @QuangAnh Anh, what is wrong with that? you saw my structure... what other solution do you recommend? – SexyMF Sep 20 '11 at 14:14
  • With a structure like that you'll have to loop through each outer dictionary. – Carra Sep 20 '11 at 14:19
  • Since I don't know why do you want to use that structure, I can't give exact suggestions. There is one thing I can say regarding Dictionary: it's performance is not good. Could be because of the .Net implementation. – Ethan Sep 20 '11 at 16:42
  • @SexyMF You could try using a [`KeyValuePair`](http://msdn.microsoft.com/en-us/library/5tbh8a42.aspx) or [`Tuple`](http://msdn.microsoft.com/en-us/library/system.tuple.aspx) instead of the inner dictionary. Thus your structure would be similar to `Dictionary>`. – Hosam Aly Dec 07 '11 at 11:35

7 Answers7

5

With LINQ, you can do something like:

return tasks.Values
            .Where(dict => dict.ContainsKey(8))
            .Select(dict => dict[8])
            .ToList();      

While this is elegant, the TryGetValue pattern is normally preferable to the two lookup operations this uses (first trying ContainsKey and then using the indexer to get the value).

If that's an issue for you, you could do something like (with a suitable helper method):

return tasks.Values
            .Select(dict => dict.TryGetValueToTuple(8))
            .Where(tuple => tuple.Item1)
            .Select(tuple => tuple.Item2)
            .ToList();  
Ani
  • 111,048
  • 26
  • 262
  • 307
4

Just iterate over all values of the first hierarchy level and use TryGetValue on the second level:

var result = new List<string>();
foreach(var inner in tasks.Values)
{
    string tmp;
    if(inner.TryGetValue(yourKey, out tmp)
        result.Add(tmp);
}

This solution has a major advantage over all other solutions presented so far: It actually uses the dictionaries of the second hierarchy level as a dictionary, i.e. the part inside the foreach loop is O(1) instead of O(n) as with all other solutions.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
3

Check this function:

   tasks.
      Where(task => task.Value.ContainsKey(8)).
      Select(task => task.Value[8]);
Somnath Muluk
  • 55,015
  • 38
  • 216
  • 226
vc 74
  • 37,131
  • 7
  • 73
  • 89
2

Daniel's solution is probably best, since it's easier to understand. But it's possible to use TryGetValue in a linq approach, too:

return tasks.Values
    .Select(dictionary => {
        string task;
        var success = dictionary.TryGetValue(yourKey, out task);
        return new { success, task };
    })
    .Where(t => t.success)
    .Select(t => t.task)
    .ToList();
phoog
  • 42,068
  • 6
  • 79
  • 117
1

Are you building tasks ?
And if I'm guessing right it's tasks[task_id]([cpu] => "task_name");
I would advice you also build cpu_tasks[cpu]([task_id] => "task_name);

static Dictionary<int, Dictionary<int, string>> cpu_tasks

It would require some more maintenance but would give you a faster run on this specific function.

Roee Gavirel
  • 18,955
  • 12
  • 67
  • 94
0
Dictionary<int, Dictionary<int, string>> tasks = new Dictionary<int, Dictionary<int, string>>();

List<string> strings = new List<string>();
foreach(var dict in tasks.Values)
{
  if(dict.ContainsKey(8))
      strings.Add(dict[8]);
}
Carra
  • 17,808
  • 7
  • 62
  • 75
  • 1
    This is pretty inefficient considering that you can use `GetValue` or `TryGetValue` rather than looping through everything in every dictionary... – Dan Puzey Sep 20 '11 at 14:13
  • 1
    Yes, your edit fixes the problem I raised. (Editing your answer and then replying as though my comment is wrong is less than endearing, however.) – Dan Puzey Sep 21 '11 at 08:19
  • Edited 21 hours ago, you replied 21 hours ago, I never saw your comment before I edited. In any case, I noticed myself that I could do it a lot faster yesterday by using a dict[8] so you were right. – Carra Sep 21 '11 at 11:28
0
Dictionary<int, Dictionary<int, string>> tasks = new Dictionary<int, Dictionary<int, string>>();
var result = string.Empty;

//more human-readable version
var searchValue = 8;
foreach (var task in tasks)
{
     if (task.Value.ContainsKey(searchValue))
         result += task.Value[searchValue];
}

//one-line version
result = tasks.ToList().Aggregate(string.Empty, (a, kvp) => a += kvp.Value.ContainsKey(searchValue) ? kvp.Value[searchValue] : string.Empty);
Trogvar
  • 856
  • 6
  • 17