Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a "DataTable" with "named row" in C#?

Tags:

c#

I need a data structure with both named column and row. For example:

magic_data_table:

        col_foo col_bar
row_foo    1       3 
row_bar    2       4

I need to be able to access elements like magic_data_table["row_foo", "col_bar"] (which will give me 3)

I also need to be able to add new columns like:

magic_data_table.Columns.Add("col_new");
magic_data_table["row_foo", "col_new"] = 5;

AFAIK, DataTable only has named column...

EDIT: I don't need to change the name of a column or a row. However, I may need to insert new rows into the middle of the table.

like image 366
GuLearn Avatar asked Jun 27 '13 16:06

GuLearn


People also ask

What is a row in a DataTable?

A data table represents a collection of things, and each individual thing is represented as a row.

What is DataTable in C?

DataTable represents relational data into tabular form. ADO.NET provides a DataTable class to create and use data table independently. It can also be used with DataSet also. Initially, when we create DataTable, it does not have table schema. We can create table schema by adding columns and constraints to the table.

How add data row to DataTable?

To add a new row, declare a new variable as type DataRow. A new DataRow object is returned when you call the NewRow method. The DataTable then creates the DataRow object based on the structure of the table, as defined by the DataColumnCollection.

How do you declare a DataRow?

To create a new DataRow, use the NewRow method of the DataTable object. After creating a new DataRow, use the Add method to add the new DataRow to the DataRowCollection. Finally, call the AcceptChanges method of the DataTable object to confirm the addition.


2 Answers

While you could use a Dictionary<string, Dictionary<string, T>> to do what you want, that wouldn't be particularly efficient in terms of memory, and would have the potential for the inner dictionaries to get out of sync. If you create your own data structure though that is a facade for lists, using dictionaries to map column names to indexes, then it's simple enough:

public class MyDataStructure<T>//TODO come up with better name
{
    private Dictionary<string, int> columns;
    private Dictionary<string, int> rows;
    private List<List<T>> data;

    public MyDataStructure(
            IEnumerable<string> rows,
            IEnumerable<string> columns)
    {
        this.columns = columns.Select((name, index) => new { name, index })
            .ToDictionary(x => x.name, x => x.index);

        this.rows = rows.Select((name, index) => new { name, index })
            .ToDictionary(x => x.name, x => x.index);

        initData();
    }

    private void initData()
    {
        data = new List<List<T>>(rows.Count);
        for (int i = 0; i < rows.Count; i++)
        {
            data.Add(new List<T>(columns.Count));
            for (int j = 0; j < columns.Count; j++)
            {
                data[i].Add(default(T));
            }
        }
    }

    public T this[string row, string column]
    {
        //TODO error checking for invalid row/column values
        get
        {
            return data[rows[row]][columns[column]];
        }
        set
        {
            data[rows[row]][columns[column]] = value;
        }
    }

    public void AddColumn(string column)
    {
        columns.Add(column, columns.Count);
        for (int i = 0; i < data.Count; i++)
        {
            data[i].Add(default(T));
        }
    }

    public void AddRow(string row)
    {
        rows.Add(row, rows.Count);
        var list = new List<T>(columns.Count);
        data.Add(list);
        for (int i = 0; i < columns.Count; i++)
        {
            list.Add(default(T));
        }
    }

    public bool RenameRow(string oldRow, string newRow)
    {
        if (rows.ContainsKey(oldRow) && !rows.ContainsKey(newRow))
        {
            this.Add(newRow, rows[oldRow]);
            this.Remove(oldRow);
            return true;
        }

        return false;
    }
}

Note that if you were willing to fix the rows/columns upon construction then you'd be able to use a T[,] as the backing for the data, which would both make the class dramatically simpler to implement, and further reduce the memory overhead, although that doesn't appear to work for your use cases.

like image 111
Servy Avatar answered Sep 30 '22 13:09

Servy


Add a column for the name - "name" in the following:

DataTable table = ...
DataColumn nameCol = table.Columns["name"];
var index = table.Rows.Cast<DataRow>()
    .ToDictionary(row => (string)row[nameCol]);

... // then when you need the values:

string rowName = ..., colName = ...
var val = index[rowName][colName];
like image 20
Marc Gravell Avatar answered Sep 30 '22 12:09

Marc Gravell