Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 4.0 - Multidimensional Associative Array (or way to mimic one?)

I'm an experienced PHP developer transitioning to C#. At present I am working on a Windows Forms application.

I found in my searches that C# doesn't support associative arrays in the same loose fashion PHP does. I have found info on Dictionary and something about "structs" which seem to be class objects.

The trouble I am having is getting my head around not only an Associative array, but a multi dimensional one that I want to use for keeping multiple counts in a series of loops.

The application is reading a text log file, searching for a predefined string, pulling out the date on that line when the string is found, and incrementing a count for that string match on that date.

In PHP, it would be as easy as this:

// Initialize
$count_array[$string_date][$string_keyword] = 0;

...

// if string is found
$count_array[$string_date][$string_keyword] += 1;

...

// To ouput contents of array
foreach($count_array as $date -> $keyword_count_array) {
    echo $date; // output date

    foreach($keyword_count_array as $keyword -> $count) {
        echo $keyword . ": " . $count;
    }
}

It seems to be a little more involved in C# (which isn't a bad thing). I have tried using an suggestion I found on another similar question but I don't really follow how to either increment or iterate/output the contents:

// Initialize
var count_array = new Dictionary<string, Dictionary<string, int>>();
count_array = null;

...

// if string is found - I think the second reference is supposed to be a Dictionary object??
count_array[string_date.ToShortDateString()][string_keyword]++;

...

// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in exportArray)
{
    foreach(KeyValuePair<string, int> kvp2 in kvp.Value) 
    {
        MessageBox.Show(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
    }
}

Am I even on the right track? Or does someone have a better/cleaner method of mimicing the PHP code above?

UPDATE

With the above C# code, I actually get an error at the "// if string is found " line. The error is "Object reference is not set to an instance of an object". I am assuming that it is because I have a string in the secound reference, not a Dictionary object. So right now, I am unsure how to increment.

UPDATE 2

Thanks everyone for your time. Current code is now functional thanks to understanding how Dictionary's work. However all advice regarding the use of classes and objects for this situation is not lost either. I may refactor to suit.

like image 246
Aaryn Avatar asked Feb 23 '11 19:02

Aaryn


3 Answers

The code itself looks sound, the only thing I see missing is there are no checks to see if the values exist before incrementing them.

Before you call

count_array[string_date.ToShortDateString()][string_keyword]++;

You'll need to do:

string shortDate = string_date.ToShortDateString();
if (!count_array.ContainsKey(shortDate))
{
    count_array.Add(shortDate, new Dictionary<string, int>());
}

if (!count_array[shortDate].ContainsKey(string_keyword))
{
    count_array[shortDate].Add(string_keyword, 0);
}

Before you try incrementing anything.

You need to initialize your dictionary entries by calling .Add or ["key"] = value. Calling ++ on an uninitialized dictionary entry won't work. Depending on what exactly it is you're trying to accomplish though it might be a good idea to use a class.

like image 67
McAden Avatar answered Sep 30 '22 00:09

McAden


You can use a tuple to create a multi-dimensional key for use in a Dictionary.

Dictionary<Tuple<TKey1,TKey2>,TValue>

Or a Dictionary of Dictionary:

Dictionary<TKey1,Dictionart<TKey2,Tvalue>>

The second one is more annoying to work with, but has the upside that you can index into it with just the first key and then get all key-value pairs associated with that key.

But perhaps you can use some linq, but your code is a bit incomplete for that.

like image 22
CodesInChaos Avatar answered Sep 30 '22 00:09

CodesInChaos


What about creating a class for this?

public class LogEntry 
{
   private List<int> _lines = new List<int>();
   public string LogContent { get;set; }
   public DateTime Time { get;set; }
   public List<int> Lines { get { return _lines; } }
}

You'd still have a dictionary of probably DateTime, LogEntry? Not entirely sure what exactly you need / what the key is.

Anyways, creating a class seems to be the "correct" way as you can express your intend more clearly.

like image 28
Maxem Avatar answered Sep 29 '22 23:09

Maxem