Table of Contents

Table<TRow, TColumn, TValue> Class

Definition

Namespace
Bodu.Collections.Generic
Assembly
Bodu.Collections.dll
Package
Bodu.Collections 1.0.0
Source
Table{T,T,T}.Views.cs

Represents a two-dimensional map that associates an ordered row/column key pair with a single value, exposing live row and column projections over a row-major backing store.

public sealed class Table<TRow, TColumn, TValue> : IEnumerable<KeyValuePair<(TRow Row, TColumn Column), TValue>>, IEnumerable where TRow : notnull where TColumn : notnull

Type Parameters

TRow

Specifies the type of row keys in the table.

TColumn

Specifies the type of column keys in the table.

TValue

Specifies the type of values stored in the table's cells.

Inheritance
Table<TRow, TColumn, TValue>
Implements
IEnumerable<KeyValuePair<(TRow Row, TColumn Column), TValue>>
Inherited Members
Extension Methods

Remarks

Table<TRow, TColumn, TValue> is the .NET analogue of Guava's Table: a map keyed by two independent keys - a row and a column - whose reason to exist is the projections. A plain Dictionary<(TRow, TColumn), TValue> already covers flat two-key lookup; adopt this type when you also need Row(TRow) ("all cells of this row") or Column(TColumn) ("this column across all rows") as first-class dictionary views, or the per-row iteration of RowMap().

The backing store is row-major - an outer dictionary from row key to an inner dictionary of that row's cells - so row-oriented operations are O(1) hash lookups. Column-oriented operations have no second index in this version: Column(TColumn), ContainsColumn(TColumn), and RemoveColumn(TColumn) scan every row and cost O(rows) per call (per enumeration for the column view), and ColumnKeys walks every cell. Choose the row axis for the key you project by most often.

The table never retains an empty row: removing a row's last cell - through Remove(TRow, TColumn) or RemoveColumn(TColumn) - also removes the row itself, so ContainsRow(TRow) and RowKeys only ever report rows that hold at least one cell.

Enumeration yields the flat cells as KeyValuePair<TKey, TValue> entries keyed by a (Row, Column) tuple, in row-major order: all cells of one row are contiguous, but the order of rows and the order of cells within a row follow the unspecified, insertion-biased order of Dictionary<TKey, TValue> - do not rely on it. The views delegate to the live backing dictionaries, so mutating the table while enumerating the table or one of its views surfaces the standard Dictionary<TKey, TValue> fail-fast behaviour (InvalidOperationException).

Table<TRow, TColumn, TValue> is not thread-safe. Concurrent reads and writes, including through the row and column views, require external synchronization.

var sales = new Table<string, int, decimal>();
sales.Add("Widgets", 2024, 1200m);
sales.Add("Widgets", 2025, 1350m);
sales.Add("Gadgets", 2025, 800m);

// The projections are the point of the type.
IReadOnlyDictionary<int, decimal> widgets = sales.Row("Widgets");     // 2024 -> 1200, 2025 -> 1350
IReadOnlyDictionary<string, decimal> in2025 = sales.Column(2025);     // Widgets -> 1350, Gadgets -> 800

sales["Gadgets", 2025] = 850m;             // upsert through the flat indexer…
decimal revised = in2025["Gadgets"];       // 850 - the held view is live

Constructors

Table()

Initializes a new instance of the Table<TRow, TColumn, TValue> class that is empty and uses the default row and column comparers.

public Table()

Table(IEqualityComparer<TRow>?, IEqualityComparer<TColumn>?)

Initializes a new instance of the Table<TRow, TColumn, TValue> class that is empty and uses the specified row and column comparers.

public Table(IEqualityComparer<TRow>? rowComparer, IEqualityComparer<TColumn>? columnComparer)

Parameters

rowComparer IEqualityComparer<TRow>

The equality comparer to use for row keys, or null to use the default comparer.

columnComparer IEqualityComparer<TColumn>

The equality comparer to use for column keys, or null to use the default comparer.

Properties

ColumnComparer

Gets the IEqualityComparer<T> used to determine column key equality.

public IEqualityComparer<TColumn> ColumnComparer { get; }

Property Value

IEqualityComparer<TColumn>

The comparer used for column identity within every row.

ColumnKeys

Gets the distinct column keys currently present in any row.

public IReadOnlyCollection<TColumn> ColumnKeys { get; }

Property Value

IReadOnlyCollection<TColumn>

A snapshot of the distinct column keys, deduplicated by ColumnComparer.

Remarks

The backing store is row-major with no column index, so each access recomputes the set by walking every cell - an O(cells) operation returning a snapshot, unlike the live RowKeys. Columns appear in row-major encounter order.

Count

Gets the total number of cells in the table.

public int Count { get; }

Property Value

int

The number of row/column/value cells across all rows, maintained as an O(1) counter.

IsEmpty

Gets a value indicating whether the table contains no cells.

public bool IsEmpty { get; }

Property Value

bool

true if the table holds no cells; otherwise, false.

this[TRow, TColumn]

Gets or sets the value of the cell at the specified row and column.

public TValue this[TRow row, TColumn column] { get; set; }

Parameters

row TRow

The row key of the cell. Must not be null.

column TColumn

The column key of the cell. Must not be null.

Property Value

TValue

Remarks

The setter upserts: assigning a new row/column pair adds the cell (creating the row when absent), while assigning an existing pair replaces its value. Use Add(TRow, TColumn, TValue) to reject duplicates instead.

Exceptions

ArgumentNullException

row or column is null.

KeyNotFoundException

The cell does not exist when read.

RowComparer

Gets the IEqualityComparer<T> used to determine row key equality.

public IEqualityComparer<TRow> RowComparer { get; }

Property Value

IEqualityComparer<TRow>

The comparer used for row identity in the row-major backing store.

RowKeys

Gets a live view of the table's row keys.

public IReadOnlyCollection<TRow> RowKeys { get; }

Property Value

IReadOnlyCollection<TRow>

A read-only collection over the backing store's row keys, reflecting later mutations.

Remarks

Because empty rows are never retained, every reported row holds at least one cell. The collection is the backing dictionary's live key collection, so its order matches the table's (unspecified) row order.

Methods

Add(TRow, TColumn, TValue)

Adds a value at the specified row and column.

public void Add(TRow row, TColumn column, TValue value)

Parameters

row TRow

The row key of the cell to add. Must not be null.

column TColumn

The column key of the cell to add. Must not be null.

value TValue

The value to store in the cell.

Remarks

Duplicate cells follow the strict Add(TKey, TValue) contract and always throw; use the indexer setter to upsert or TryAdd(TRow, TColumn, TValue) to stay non-throwing. Adding to an absent row creates the row.

Exceptions

ArgumentNullException

row or column is null.

ArgumentException

The table already contains a cell at the specified row and column.

Clear()

Removes all cells from the table.

public void Clear()

Column(TColumn)

Returns a live read-only dictionary view over the specified column's cells across all rows, keyed by row.

public IReadOnlyDictionary<TRow, TValue> Column(TColumn column)

Parameters

column TColumn

The column key to project. Must not be null.

Returns

IReadOnlyDictionary<TRow, TValue>

A read-only dictionary from row key to cell value for the column; empty when no row holds the column.

Remarks

The view holds the table and the column key and resolves against the live backing store on every access, so it reflects all later mutations, including a view created before any row holds the column. Each call allocates a new lightweight view instance.

The backing store is row-major with no column index, so the view's aggregate members are honest about their cost: Count and each full enumeration scan every row and are O(rows) per access, while the per-row lookups (the indexer, ContainsKey, TryGetValue) are O(1). Mutating the table while enumerating the view surfaces the standard Dictionary<TKey, TValue> fail-fast behaviour (InvalidOperationException).

Exceptions

ArgumentNullException

column is null.

Contains(TRow, TColumn)

Determines whether the table contains a cell at the specified row and column.

public bool Contains(TRow row, TColumn column)

Parameters

row TRow

The row key to locate. Must not be null.

column TColumn

The column key to locate. Must not be null.

Returns

bool

true if the cell exists; otherwise, false.

Exceptions

ArgumentNullException

row or column is null.

ContainsColumn(TColumn)

Determines whether the table contains at least one cell in the specified column.

public bool ContainsColumn(TColumn column)

Parameters

column TColumn

The column key to locate. Must not be null.

Returns

bool

true if any row holds a cell in the column; otherwise, false.

Remarks

The backing store is row-major with no column index, so this operation scans every row and is O(rows), unlike the O(1) ContainsRow(TRow).

Exceptions

ArgumentNullException

column is null.

ContainsRow(TRow)

Determines whether the table contains at least one cell in the specified row.

public bool ContainsRow(TRow row)

Parameters

row TRow

The row key to locate. Must not be null.

Returns

bool

true if the row holds at least one cell; otherwise, false.

Remarks

Because empty rows are never retained, this is an O(1) hash lookup that is true exactly when the row has one or more cells.

Exceptions

ArgumentNullException

row is null.

ContainsValue(TValue)

Determines whether the table contains a cell with the specified value.

public bool ContainsValue(TValue value)

Parameters

value TValue

The value to locate. The value can be null for reference types.

Returns

bool

true if any cell holds a value equal to value; otherwise, false.

Remarks

This operation scans every cell and is O(cells). Value equality is determined by Default for TValue.

GetEnumerator()

Returns an enumerator that iterates over the table's cells in row-major order.

public IEnumerator<KeyValuePair<(TRow Row, TColumn Column), TValue>> GetEnumerator()

Returns

IEnumerator<KeyValuePair<(TRow Row, TColumn Column), TValue>>

An enumerator over the flat cells as KeyValuePair<TKey, TValue> entries keyed by a (Row, Column) tuple.

Remarks

All cells of one row are contiguous, but the order of rows and of cells within a row follows the unspecified, insertion-biased order of Dictionary<TKey, TValue> - do not rely on it. Mutating the table while enumerating surfaces the standard dictionary fail-fast behaviour (InvalidOperationException).

Remove(TRow, TColumn)

Removes the cell at the specified row and column.

public bool Remove(TRow row, TColumn column)

Parameters

row TRow

The row key of the cell to remove. Must not be null.

column TColumn

The column key of the cell to remove. Must not be null.

Returns

bool

true if the cell was found and removed; otherwise, false.

Remarks

Removing a row's last cell also removes the row itself, so ContainsRow(TRow) and RowKeys never report an empty row.

Exceptions

ArgumentNullException

row or column is null.

RemoveColumn(TColumn)

Removes the specified column's cell from every row.

public bool RemoveColumn(TColumn column)

Parameters

column TColumn

The column key whose cells to remove. Must not be null.

Returns

bool

true if at least one cell was removed; otherwise, false.

Remarks

The backing store is row-major with no column index, so this operation scans every row and is O(rows). Rows whose last cell was in the removed column are pruned, so ContainsRow(TRow) and RowKeys never report an empty row.

Exceptions

ArgumentNullException

column is null.

RemoveRow(TRow)

Removes all cells in the specified row.

public bool RemoveRow(TRow row)

Parameters

row TRow

The row key whose cells to remove. Must not be null.

Returns

bool

true if the row existed and its cells were removed; otherwise, false.

Exceptions

ArgumentNullException

row is null.

Row(TRow)

Returns a live read-only dictionary view over the cells of the specified row, keyed by column.

public IReadOnlyDictionary<TColumn, TValue> Row(TRow row)

Parameters

row TRow

The row key to project. Must not be null.

Returns

IReadOnlyDictionary<TColumn, TValue>

A read-only dictionary from column key to cell value for the row; empty when the row is absent.

Remarks

The view holds the table and the row key and resolves the row on every access, so it is fully live: cells added or removed after the view was created are reflected, a view created before the row exists reports empty and starts reporting cells once the row appears, and it reverts to empty when the row's last cell is removed. Each call allocates a new lightweight view instance.

Every member is an O(1) delegation to the row's live cell dictionary. Mutating the table while enumerating the view surfaces the standard Dictionary<TKey, TValue> fail-fast behaviour (InvalidOperationException).

Exceptions

ArgumentNullException

row is null.

RowMap()

Returns a lazy sequence pairing each row key with a live row view over that row's cells.

public IEnumerable<KeyValuePair<TRow, IReadOnlyDictionary<TColumn, TValue>>> RowMap()

Returns

IEnumerable<KeyValuePair<TRow, IReadOnlyDictionary<TColumn, TValue>>>

A sequence of KeyValuePair<TKey, TValue> entries associating each row key with the same live view that Row(TRow) returns for it.

Remarks

The sequence enumerates the backing store's row keys lazily, so mutating the table while iterating surfaces the standard Dictionary<TKey, TValue> fail-fast behaviour (InvalidOperationException). Because empty rows are never retained, every yielded view holds at least one cell at the moment it is yielded.

TryAdd(TRow, TColumn, TValue)

Attempts to add a value at the specified row and column without throwing on a duplicate cell.

public bool TryAdd(TRow row, TColumn column, TValue value)

Parameters

row TRow

The row key of the cell to add. Must not be null.

column TColumn

The column key of the cell to add. Must not be null.

value TValue

The value to store in the cell.

Returns

bool

true if the cell was added; false if the table already contains a cell at the specified row and column, in which case the table is unchanged.

Exceptions

ArgumentNullException

row or column is null.

TryGetValue(TRow, TColumn, out TValue)

Attempts to retrieve the value of the cell at the specified row and column.

public bool TryGetValue(TRow row, TColumn column, out TValue value)

Parameters

row TRow

The row key of the cell. Must not be null.

column TColumn

The column key of the cell. Must not be null.

value TValue

When this method returns, contains the value of the cell, if the cell is found; otherwise, the default value for the type of the value parameter.

Returns

bool

true if the table contains the cell; otherwise, false.

Exceptions

ArgumentNullException

row or column is null.

Explicit Interface Implementations

IEnumerable.GetEnumerator()

Returns an enumerator that iterates through a collection.

IEnumerator IEnumerable.GetEnumerator()

Returns

IEnumerator

An IEnumerator object that can be used to iterate through the collection.

Applies to

ProductVersions
.NET8, 10