LINQ Short Takes – Number 3 –LINQ over Multiple Dimension Arrays and Lists


Introduction

This blog post presents an alternative approach to using LINQ against List<Of T> and multidimensional arrays (and other types). This approach builds on the preceding blog posts LINQ Short Takes – Number 1 – Enumerable.Range() and LINQ Short Takes – Number 2 – Using Method Syntax to Create a Cartesian Product.

An Alternative Approach to Multiple Dimension Arrays and Lists

A core of this approach is to use two features of the C# language. These features are:

  1. The indexer which is available on some object types (see MSDN Article: Indexers (C# Programming Guide)), and the indexed access which the array objects support. In the case of object collections which do not support the IList(of T) interface, the LINQ extension methods ElementAt provides an equivalent, and very useable, alternative.
  2. The use of LINQ to generate the index values that are used against the collection objects supporting indexers, and array objects.

The use of LINQ to generate the index values into collections or arrays is not the approach that developers normally use with these objects. This approach has benefits in terms of clarity of the resulting code. This approach is only applicable to certain classes of problems that a developer may encounter.

Example Notes:

There are a couple of points to note about the presented examples:

  1. I have used my LINQ extension method ToOutput to produce a dump of the result sequences. This extension method was the subject of my blog post Dumping a formatted IEnumerable to Output. This blog post also includes to the source code for the extension method. Additionally, there are links to docx and pdf files of the source code.
  2. The source code for the methods which the following examples contain are available in docx and pdf files from the following URLS:
    http://craigwatson1962.files.wordpress.com/2012/02/linq-over-multiple-dimension-arrays-and-lists.docx
    http://craigwatson1962.files.wordpress.com/2012/02/linq-over-multiple-dimension-arrays-and-lists.pdf

Examples of Approach on List Collections

The following are examples of LINQ query syntax examples of using LINQ to generate and enumerate the index into a List. By using the array, or indexer, syntax to access the elements of the List(Of T) Class makes calculating the difference between adjacent dates in the List(Of T) Class trivial. The pure LINQ way to achieve this type of calculation between members of a List is not as simple, clear, or concise, as this approach.

private void LINQ_ALternative_List()
{
    List<DateTime> Dates = new List<DateTime>()
    {
        new DateTime(2012,1,1), new DateTime(2012,2,29),
        new DateTime(2012,8,13), new DateTime(2012,9,10)
    };
    var DaysGaps = from idx in Enumerable.Range(0, Dates.Count-1)
                   select Dates[idx + 1] - Dates[idx];
    Debug.WriteLine("Dumping DaysGaps");
    DaysGaps.ToOutput(FormatFunction:
        (val, position) => string.Format("[{0}]={1}\n", position, val.ToString("%d")));

    var DaysGaps1 = from idx in Enumerable.Range(0, Dates.Count - 1)
                    select (Dates[idx + 1] - Dates[idx]).Days;
    Debug.WriteLine("Dumping DaysGaps1");
    DaysGaps1.ToOutput(FormatFunction:
        (val, position) => string.Format("[{0}]={1}\n", position, val));
    return; // Allows a breakpoint at the end of the method. }

Example of Approach on Dictionary Collection

The following example demonstrates the use of ElementAt method to achieve the same days difference calculation as the preceding example. This example is manipulating the values in the Value property of the KeyValuePair contained in a Dictionary(Of TKey, TValue) Class.

private void LINQ_Alternative_Dictionary()
{
    Dictionary<int, DateTime> Dict1 = new Dictionary<int, DateTime>()
    {
        {1, new DateTime(2012, 1,  1)}, {5, new DateTime(2012, 3, 15)},
        {7, new DateTime(2012, 4, 21)}, {9, new DateTime(2012, 12, 25)}
    };
    var a = Dict1.ElementAt(2);
    var DateDiffs = from idx in Enumerable.Range(0, Dict1.Count - 1)
                    select (Dict1.ElementAt(idx + 1).Value - Dict1.ElementAt(idx).Value).Days;
    Debug.WriteLine("Dumping DateDiffs");
    DateDiffs.ToOutput(FormatFunction:
        (val, position) => string.Format("[{0}]={1}\n", position, val));

    return; // Allows a breakpoint at the end of the method. }

Examples of Approach on Multidimensional Arrays

The following is an example of using LINQ query syntax to access the elements of a 2-dimensional array. The code forms a Cartesian product between the sequences idx1 and idx2. This Cartesian product is used to access each of the elements of the array Multi1. The example creates an anonymous type (see MSDN article: Anonymous Types (C# Programming Guide) for further information) which contains the indexes and array element value.

There are a couple of other notable points:

  1. The use of the Array.GetLength Method to discover the number of elements that are required in the array index sequences. The use of the Array.GetLength Method results in code that is more robust.
  2. This example demonstrates accessing a 2-dimensional array. A similar pattern will work for any number of array dimensions.
  3. 3. Arrays with more than one dimension do not support the IEnumerable(Of T) Interface. This results in multidimensional arrays not natively, or supported by the implementation of the .Net Framework (see MSDN article: Overview of the .NET Framework for further information). This results in multidimensional arrays not being usable as a LINQ data source. If you attempt to use a multidimensional array as a data source (from ) results in the error CS1935. The following is the full error message
    error CS1935: Could not find an implementation of the query pattern for source type ‘int[*,*]‘. ‘Select’ not found. Are you missing a reference to ‘System.Core.dll’ or a using directive for ‘System.Linq’?
private void LINQ_ALternative_Array()
{
    int[,] Multi1 = new int[,]
    {
        {1,2,3}, {4,5,6}, {7,8,9}
    };
    var dump1 = from idx1 in Enumerable.Range(0, Multi1.GetLength(0))
                from idx2 in Enumerable.Range(0, Multi1.GetLength(1))
                select new { idx1, idx2, val = Multi1[idx1, idx2] };
    Debug.WriteLine("Dumping dump1");
    dump1.ToOutput(FormatFunction:
        (val, position) => string.Format("[{0}] [{1},{2}]={3}\n", position, val.idx1, val.idx2, val.val));

    return; // Allows a breakpoint at the end of the method. }

Conclusion

I hope that this blog post and the preceding two blog posts in this series LINQ Short Takes – Number 1 – Enumerable.Range() and LINQ Short Takes – Number 2 – Using Method Syntax to Create a Cartesian Product have added something useful to your kit bag of LINQ tools and techniques.

The approach to working with collections and arrays through an index (or indexes) is one that is applicable to a class of problems that I have not seen used elsewhere. That is not to says that I have conducted an exhaustively search of the web for other examples of this approach.

For those readers who have read LINQ Short Takes – Number 2 – Using Method Syntax to Create a Cartesian Product, and are interested. I indicted in that blog post I would think about an extension method implementation of a general solution to creation of Cartesian products using the Enumerable.Join Method. Well, I wrote that extension method this morning, and will ‘clean it up’, write some XML documentation, and post the solution on this blog sometime soon.

, , , , , , , , , , , , ,

  1. LINQ Pivot, or Crosstab, Extension Method « Craig's Eclectic Blog
  2. C# Short Takes – 2 – String from IEnumerable « Craig's Eclectic Blog
  3. LINQ Short Takes – Number 4 –Make Union into a UnionAll « Craig's Eclectic Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: