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
TRowSpecifies the type of row keys in the table.
TColumnSpecifies the type of column keys in the table.
TValueSpecifies the type of values stored in the table's cells.
- Inheritance
-
Table<TRow, TColumn, TValue>
- Implements
- 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
rowComparerIEqualityComparer<TRow>The equality comparer to use for row keys, or null to use the default comparer.
columnComparerIEqualityComparer<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
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
rowTRowThe row key of the cell. Must not be null.
columnTColumnThe 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
roworcolumnis 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
rowTRowThe row key of the cell to add. Must not be null.
columnTColumnThe column key of the cell to add. Must not be null.
valueTValueThe 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
roworcolumnis 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
columnTColumnThe 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
columnis 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
rowTRowThe row key to locate. Must not be null.
columnTColumnThe column key to locate. Must not be null.
Returns
Exceptions
- ArgumentNullException
roworcolumnis null.
ContainsColumn(TColumn)
Determines whether the table contains at least one cell in the specified column.
public bool ContainsColumn(TColumn column)
Parameters
columnTColumnThe column key to locate. Must not be null.
Returns
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
columnis null.
ContainsRow(TRow)
Determines whether the table contains at least one cell in the specified row.
public bool ContainsRow(TRow row)
Parameters
rowTRowThe row key to locate. Must not be null.
Returns
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
rowis null.
ContainsValue(TValue)
Determines whether the table contains a cell with the specified value.
public bool ContainsValue(TValue value)
Parameters
valueTValueThe value to locate. The value can be null for reference types.
Returns
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
rowTRowThe row key of the cell to remove. Must not be null.
columnTColumnThe column key of the cell to remove. Must not be null.
Returns
Remarks
Removing a row's last cell also removes the row itself, so ContainsRow(TRow) and RowKeys never report an empty row.
Exceptions
- ArgumentNullException
roworcolumnis null.
RemoveColumn(TColumn)
Removes the specified column's cell from every row.
public bool RemoveColumn(TColumn column)
Parameters
columnTColumnThe column key whose cells to remove. Must not be null.
Returns
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
columnis null.
RemoveRow(TRow)
Removes all cells in the specified row.
public bool RemoveRow(TRow row)
Parameters
rowTRowThe row key whose cells to remove. Must not be null.
Returns
Exceptions
- ArgumentNullException
rowis 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
rowTRowThe 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
rowis 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
rowTRowThe row key of the cell to add. Must not be null.
columnTColumnThe column key of the cell to add. Must not be null.
valueTValueThe 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
roworcolumnis 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
rowTRowThe row key of the cell. Must not be null.
columnTColumnThe column key of the cell. Must not be null.
valueTValueWhen 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
Exceptions
- ArgumentNullException
roworcolumnis 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
| Product | Versions |
|---|---|
| .NET | 8, 10 |