views:

691

answers:

6

I want to build a data structure which is basically a matrix of strings with the following.

  1. A growable no of rows
  2. A FIXED no of columns

I want to be able to get at whatever is in a particular row or column via a method call that takes the an integer row no and an int col number as an argument. In addition I want to be able to set the value of a column which using the row and col no.

I could do this myself but am open to seeing what other people might do...

EDIT Sorry the column list will be fixed, my mistake :-(

+3  A: 

Sounds to me like a data table would be the easiest opion.

Kane
+5  A: 

The DataTable and the DataSet (basically a collection of data tables) would work fine for what you are looking for.

You could then access your data (once you set up the columns and added your rows of data) via the following syntax:

datatable.rows(index)("ColumnName")

or

datatable.rows(rowindex)(columnindex)
TheTXI
+2  A: 

How about basing something on Dictionary<System.Drawing.Point, string>? So that you can write;

stringGrid.Add(new Point(3,4), "Hello, World!");

Create a class containing such a dictionary and then you get what you're looking for, almost for free. Something like (untested)

class StringGrid
{
    Dictionary<System.Drawing.Point, string> grid;

    public StringGrid
    {
        this.grid = new Dictionary<System.Drawing.Point, string>();
    }

    public string Get(int x, int y)
    {
        string result = null;
        grid.TryGetValue(new Point(x,y), out result);
        return result;
    }

    public void Set(int x, int y, string content)
    {
        var pos = new Point(x,y);
        if (grid.ContainsKey(pos))
        {
            if (content == null)
            {
                // no content, so just clear the cell.
                grid.remove(pos);
            }
            else
            {
                // add new content
                grid[pos].Value = content;
            }  
        } 
        else if (content != null)
        {
            // new non-null content
            grid.add(pos, content);
        }
    }
}

EDIT: Also, if you want to get really flash;

  • replace the dictionary with a SortedList<,>
  • replace System.Drawing.Point with your own struct which implements IComparable

This way, the list would be internally ordered by row, then by column, making a simple foreach loop sufficient to iterate through all the values in the first row, then the values in the second, and so on. Allows you to convert to and `IEnumerable`` -- or a collection of rows.

Steve Cooper
Two things: 1) use indexers :) and 2) a dictionary of points doesn't keep the matrix rectangular. I am assuming #2 matters.
Colin Burnett
This is actually kind of nice. And it'd be easy to roll your own indexers for it. The rectangularity thing, though...I suppose you could keep things "aligned" as columns or rows are added, but it'd be a bit of work. Easier if it's allowed to be jagged.
Michael Todd
More or less my idea too, but I would rather not use Drawing.Point for this. It's so out of contect. But technically, it's OK and ready to use.
Henk Holterman
Drawing.Point -- yeah, it's probably better to create your own Struct (it must be a struct or the dictionary wont work) but it would be exactly the same struct as Drawing.Point.rectangular matrix -- this will grow limitlessly, as a pure sparse matrix. It doesn't need to worry about it. It does require a O(n) operation to find the bounding box dimensions, if that's what's required, but that wasn't stated.
Steve Cooper
@Steve, you can certainly use objects in a Dictionary as keys. You need to implement Equals and GetHashCode, IEquatable<T>, or specify an IEqualityComparer<T> to the Dictionary constructor to do so.
Colin Burnett
@Colin - you're absolutely right; you can use a class. What I was trying to say was that `struct point { int x; int y }` works as a key, `class point { int x; int y }` will not.
Steve Cooper
+3  A: 

If you need more than two dimensions, or want a more generic solution, you could possibly make it work with a List<List<T>>, nested as deeply as required, and wrapped in a custom type if you need more specific functionality or want simpler method calls.

Tomas Lycken
if they have a key or Id you might want to use SortedList<int,List<T>> for faster access.
Marcom
+2  A: 

If I wanted/needed to roll my own (say I didn't want to deal with what DataTable/DataSet) then I'd write something around a List<List<T>> (or List<List<object>>) as a list of rows then columns with logic to keep the lists rectangular.

  • AddRow() would add a new outer list entry.
  • AddColumn() would add a new item to all lists in the inner list.
  • this[int row, int col] would access this._Data[row][col].

Something like that.

Switch to a Dictionary<K, List<V>> if I wanted named columns (then the lists contain row data for that column).

Colin Burnett
+2  A: 

If you want it to be expandable in both directions then a DataTable isn't the best of choices.

For the interface, you can use an indexer property:

class Foo 
{   
    public string this[int x, int y]
    {
        get { return GetData(x,y); }
    }
}

For the backend Storage, the best option depends a bit on expected usage, ie will there be lots of empty cells. You could define a

struct Index { public readonly int X, Y; }

and override the Equals() and GetHashCode() members. There have been several questions about this on SO recently.

After that, use a Dictionary < Index, string>

You'd still have to manage Row and Column limits, and maybe intercept getting of empty cells.

Henk Holterman