Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to create a multidimensional strongly typed data structure?

I need a multi-dimensional data structure, where each dimension is a small list which is known at design time.

At different places in my program, I'd like to be able to access the data "sliced" by different dimensions, in strongly-typed fashion.

I've put some sample code below that works for a 2D example using nested interfaces, but I imagine it would get pretty horrendous in 3D or 4D. As @kvb identified, the boilerplate code required would grow exponentially.

Does anyone have a better suggestion? By which I mean, keeping the code required simple/short/easy to understand, while still retaining the ability to do things along the following lines:

Data a = new Data(...)
...
SomeMethodThatOnlyCaresAboutRedThings(a.Red) // takes a IBySize<T>
...
SomeMethodThatOnlyCaresAboutBigThings(a.Big) // takes a IByColour<T>
...

This avoids those methods having to know about parts of the data structure that aren't relevant to them, hence making them more easily testable.

I've used colours/sizes here purely as an example, apologies for inadvertently misleading anyone that these choices were meaningful. T could be a simple data item like a float or some other simple data structure.

Tagged as F# and C# as I'd be happy with a solution in either.

public interface IByColour<T>
{
    T Green { get; }
    T Red { get; }
    T Blue { get; }
}

public interface IBySize<T>
{
    T Small { get; }
    T Big { get; }
}

internal class ByColour<T> : IByColour<T>
{
    public T Green { get; private set; }
    public T Red { get; private set; }
    public T Blue { get; private set; }

    internal ByColour(T green, T red, T blue)
    {
        Green = green;
        Red = red;
        Blue = blue;
    }
}

internal class BySize<T> : IBySize<T>
{
    public T Small { get; private set; }
    public T Big { get; private set; }

    internal BySize(T small, T big)
    {
        Small = small;
        Big = big;
    }
}

public class Data<T> : IByColour<IBySize<T>>, IBySize<IByColour<T>>
{
    public IBySize<T> Green { get; private set; }
    public IBySize<T> Red { get; private set; }
    public IBySize<T> Blue { get; private set; }

    public IByColour<T> Small { get; private set; }
    public IByColour<T> Big { get; private set; }

    public Data(IBySize<T> green, IBySize<T> red, IBySize<T> blue)
    {
        Green = green;
        Red = red;
        Blue = blue;

        Small = new ByColour<T>(Green.Small, Red.Small, Blue.Small);
        Big = new ByColour<T>(Green.Big, Red.Big, Blue.Big);
    }
}

EDIT: to clarify what I mean by "better", a desirable property my solution has, and explain how I'd like to use it.

like image 504
Mark Pattison Avatar asked Oct 18 '12 16:10

Mark Pattison


People also ask

What are the advantages of multidimensional database?

Some of the advantages in Multidimensional database are: Increased Performance: The performance is much better than that of normal databases such as the relational database. Better Data Presentation: The data in a multi-faceted and contains many different factors. The data presentation is great distance superior to conventional databases.

What is a multi-dimensional data model?

The multi-Dimensional Data Model is a method which is used for ordering data in the database along with good arrangement and assembling of the contents in the database.

Is it possible to deal with multi-dimensional datasets?

However, dealing with multi-dimensional datasets with typically more than two attributes start causing problems, since our medium of data analysis and communication is typically restricted to two dimensions.

What is an example of a multidimensional array?

The data is stored as a record in a row and each record divided into columns. Below are the examples of the multidimensional array: MDB – Multidimensional Database: It is a type of database that has the data warehouse and OLAP (online analytical processing).


1 Answers

This sounds like a good use of a good old fashioned DataTable. Then you can use Linq to slice and dice however you want, and any unique types created by different combinations of columns selected are generated automatically by the compiler. All the columns in a DataTable are strongly typed, as are results of queries against them. Also, the DataColumns in a DataTable can have any type at all, including complex objects or you own enumeration types.

If you want to stick with a more mathy / immutable / F# way of doing things, you could use an array or List of Tuple<Type1, Type2, .. TypeN>, which is basically the same thing as a DataTable anyway.

If you gave a little more background on what you're modeling I could provide an example. I'm not sure if the code you posted is supposed to represent clothes, images (RGB color space) or something completely different.

[An hour later] Well, no update from the OP so I'll proceed with an example where I use List<Tuple<x, y, ..n>> and assume the objects are clothing items.

// Some enums
public enum Size { Small, Medium, Large }
public enum Color { Red, Green, Blue, Purple, Brown }
public enum Segment { Men, Women, Boys, Girls, Infants }

// Fetches the actual list of items, where the object
// item is the actual shirt, sock, shoe or whatever object
static List<Tuple<Size, Color, Segment, object>> GetAllItems() {
    return new List<Tuple<Size, Color, Segment, object>> {
        Tuple.Create(Size.Small, Color.Red, Segment.Boys, (object)new { Name="I'm a sock! Just one sock." }),
        Tuple.Create(Size.Large, Color.Blue, Segment.Infants, (object)new { Name="Baby hat, so cute." }),
        Tuple.Create(Size.Large, Color.Green, Segment.Women, (object)new { Name="High heels. In GREEN." }),
    };
}

static void test() {
    var allItems = GetAllItems();

    // Lazy (non-materialized) definition of a "slice" of everything that's Small
    var smallQuery = allItems.Where(x => x.Item1 == Size.Small);

    // Lazy map where the key is the size and the value is 
    // an IEnumerable of all items that are of that size
    var sizeLookup = allItems.ToLookup(x => x.Item1, x => x);

    // Materialize the map as a dictionary the key is the size and the 
    // value is a list of all items that are of that size
    var sizeMap = sizeLookup.ToDictionary(x => x.Key, x => x.ToList());

    // Proof:
    foreach (var size in sizeMap.Keys) {
        var list = sizeMap[size];
        Console.WriteLine("Size {0}:", size);
        foreach (var item in list) {
            Console.WriteLine("  Item: {{ Size={0}, Color={1}, Segment={2}, value={3} }}",
                item.Item1, item.Item2, item.Item3, item.Item4);
        }
    }
}
like image 86
Joshua Honig Avatar answered Sep 28 '22 08:09

Joshua Honig