CrossRefCollection - Add A Multi-Dimensional Dictionary Collection To The Language

I’m building a library to identify audio encodings used in a byte stream and there’s a lot of look-up tables required to determine bit rates, sample sizes, and so on. So, I decided to build a little extension to System.Collections.Generic that creates a multi-dimensional Dictionary collection that allows for strongly typed row/column keys and data values accessing data by a multi-parameter indexer function.

Introduction

 
The moral of this article is that you should never be afraid to extend the language. It’s something I always try to impress upon newcomers to C#, that if they’re facing repetitive tasks or find themselves putting the same methods into their classes over and over, they can just add an extension method and neatly wrap up their utility functions.

Currently, I’m building a library to identify audio encodings used in a byte stream and there’s a lot of look-up tables required to determine bit rates, sample sizes and so on. So, I decided to build a little extension to System.Collections.Generic that creates a multi-dimensional Dictionary collection that allows for strongly typed row/column keys and data values accessing data by a multi-parameter indexer function. The collection listed below is the result of my work and it can be found on NuGet here with the code available openly with a little console class to demonstrate it, available here.

CrossReference<TRows, TColumns, TData>

Add a generic collection called CrossReference to the System.Collections.Generic namespace that allows the creation of a multi-dimensional keyed dictionary. Values in the table are accessed by providing a row and column key. For example, if you had a collection called PrimaryColours with the following data saved in it;

 RedGreenBlue
RedRedYellowMagenta
GreenYellowGreenCyan
BlueMagentaCyanBlue

the following reference PrimaryColours[“Blue”, “Green”] will return “Cyan”. The collection allows for strongly typing the row key, column key, and data values when initializing. 

  1. CrossReference<string, int, bool> _TestTable = new CrossReference<string, int, bool>();    

For example, the above code creates a collection where the row keys are strings, the column keys are integers and the data values are booleans i.e.:

 123
Xtruefalsefalse
Yfalsetruefalse
Zfalsefalsetrue

with [“X”,3] = false and [“Y”, 2] = true.

Set Up

As this is an extension to the System.Collections.Generic namespace, only that specific library will be required.

  1. using System.Collections.Generic;    

Initialising

There are a number of ways to create and load values into the collection.

Example 1

Initializing a blank object and then passing a complete object[,] to the Table parameter to fill in row and column keys and all data values. In this example, the table data represents the bit rate of an MP3 based on values taken from the header of the MP3 file. The column headers represent the MPEG version and the ‘Layer’ and the rows represent the 4 bits taken from the file that denotes the bitrate of the audio.

  1. CrossReference<intintint> _TestTable = new CrossReference<intintint>();    
  2.     
  3. _TestTable.Table = new object[,]    
  4. {    
  5.     { null, 1111, 1110, 1101, 1011, 1010, 1001 },    
  6.     { 0000,    0,    0,    0,    0,    0,    0 },    
  7.     { 0001,   32,   32,   32,   32,    8,    8 },    
  8.     { 0010,   64,   48,   40,   48,   16,   16 },    
  9.     { 0011,   96,   56,   48,   56,   24,   24 },    
  10.     { 0100,  128,   64,   56,   64,   32,   32 },    
  11.     { 0101,  160,   80,   64,   80,   40,   40 },    
  12.     { 0110,  192,   96,   80,   96,   48,   48 },    
  13.     { 0111,  224,  112,   96,  112,   56,   56 },    
  14.     { 1000,  256,  128,  112,  128,   64,   64 },    
  15.     { 1001,  288,  160,  128,  144,   80,   80 },    
  16.     { 1010,  320,  192,  160,  160,   96,   96 },    
  17.     { 1011,  352,  224,  192,  176,  112,  112 },    
  18.     { 1100,  384,  256,  224,  192,  128,  128 },    
  19.     { 1101,  416,  320,  256,  224,  144,  144 },    
  20.     { 1110,  448,  384,  320,  256,  160,  160 },    
  21.     { 1111,   -1,   -1,   -1,   -1,   -1,   -1 }    
  22. };    

Example 2

This is a simple 3x3 grid with a string as the row key, an int as the column key and booleans as the data type. After the grid has been initialized and 3 of the values changed to true (the default value of the data will be false) new columns keys are set which has the effect of resetting all of the data.

  1. CrossReference<string, int, bool> _TestTableB = new CrossReference<string, int, bool>(new string[] { "A""B""C" }, new int[] { 1, 2, 3 });    
  2.     
  3. _TestTableB["A", 1] = true;    
  4. _TestTableB["B", 2] = true;    
  5. _TestTableB["C", 3] = true;    
  6.     
  7. _TestTableB.Columns = new List<int>() { 9, 8, 7 };    

Example 3

This is a similar example to the previous one but it demonstrates a different method for setting and resetting the column keys. Row keys can be reset in the exact same way.

  1. CrossReference<string, string, double> _TestTableC = new CrossReference<string, string, double>();    
  2.     
  3. _TestTableC.AddColumns(new string[] { "X""Y""Z" });    
  4. _TestTableC.AddRows(new string[] { "X""Y""Z" });    
  5.     
  6. _TestTableC["X""Z"] = 1.5;    
  7. _TestTableC["Y""Y"] = 1.5;    
  8. _TestTableC["Z""X"] = 1.5;    
  9.     
  10. _TestTableC.AddColumns("A""B""C");    
  11.     
  12. _TestTableC["X""A"] = 2.1;    
  13. _TestTableC["Y""B"] = 2.1;    
  14. _TestTableC["Z""C"] = 2.1;    

Additional Methods

 
Clear

.Clear() resets the collection, removing any row keys, column keys, and data.

Keys

.Rows() and .Columns() return a List collection, strongly types to the row and column key types declared.

.ContainsRowKey(TRows key) and .ContainsColumnKey(TColumns key) return a boolean to indicate if the passed key value exists in the respective collection.