/// <summary>Populate table headers based on the specified layout.</summary> private void PopulateHeaders(VariantMatrix result) { MatrixLayout layout = result.Layout; // Populate row headers if they are specified by the layout if (layout.HasRowHeaders()) { var rowHeaders = new List <string>(); for (int rowIndex = 0; rowIndex < result.RowCount; rowIndex++) { rowHeaders.Add($"Row{rowIndex}"); } result.RowHeaders = rowHeaders.ToArray(); } // Populate column headers if they are specified by the layout if (layout.HasColHeaders()) { var colHeaders = new List <string>(); for (int colIndex = 0; colIndex < result.ColCount; colIndex++) { colHeaders.Add($"Col{colIndex}"); } result.ColHeaders = colHeaders.ToArray(); } // Populate corner header if it is specified by the layout if (layout.HasCornerHeader()) { result.CornerHeader = "Corner"; } }
/// <summary> /// Create data containers inside the matrix and populate the values /// with default(T). /// /// This method also creates the row and column headers with the /// correct size, if the respective header is specified in the layout. /// </summary> public void Resize(MatrixLayout layout, int rowCount, int colCount) { Layout = layout; RowCount = rowCount; ColCount = colCount; if (layout.HasRowHeaders()) { RowHeaders = new string[rowCount]; } if (layout.HasColHeaders()) { ColHeaders = new string[colCount]; } }
/// <summary> /// Populate by parsing multi-line CSV text using the specified /// matrix layout and an array of column parser functions. /// /// If the data has more columns than the array of parsers, /// the last parser in the array is used for all additional /// columns. This permits ingesting CSV files with an unknown /// number of value columns of the same type, after an initial /// set of category columns that have other types. /// /// The specified parser is not used for row and column headers /// which are always dot delimited strings. /// </summary> public void ParseCsv(MatrixLayout layout, Func <string, T>[] colParsers, string csvText) { if (layout == MatrixLayout.Empty) { throw new Exception("Matrix layout passed to ParseCsv method is empty"); } Layout = layout; // Parse into a list of text lines string[] csvLines = CsvUtil.TextToLines(csvText); // Parse each line into tokens, keeping track of maximum // size which will determine the matrix size int rowCount = csvLines.Length; List <string[]> parsedRows = new List <string[]>(); int colCount = 0; foreach (string csvLine in csvLines) { string[] tokens = CsvUtil.LineToTokens(csvLine); if (colCount < tokens.Length) { colCount = tokens.Length; } parsedRows.Add(tokens); } int colOffset = 0; if (layout.HasRowHeaders()) { // First column is row headers, data has one less column colOffset = 1; colCount--; } int rowOffset = 0; if (layout.HasColHeaders()) { // First row is column headers, data has one less row rowOffset = 1; rowCount--; } // Resize Resize(layout, rowCount, colCount); // Parse column headers if present if (rowOffset != 0) { string[] rowTokens = parsedRows[0]; // Populate corner header if present if (colOffset != 0) { CornerHeader = rowTokens[0]; } // Populate column headers if present for (int colIndex = 0; colIndex < colCount; ++colIndex) { string token = rowTokens[colOffset + colIndex]; ColHeaders[colIndex] = token; } } for (int rowIndex = 0; rowIndex < rowCount; ++rowIndex) { string[] rowTokens = parsedRows[rowOffset + rowIndex]; // Set row header if present if (colOffset != 0) { RowHeaders[rowIndex] = rowTokens[0]; } // Set row values for (int colIndex = 0; colIndex < colCount; ++colIndex) { // If column index is outside the range of column parsers array, // take the last element of the array instead. This permits // parsing CSV data with unknown number of columns where trailing // columns have the same type int parserColIndex = colIndex < colParsers.Length ? colIndex : colParsers.Length - 1; Func <string, T> colParser = colParsers[parserColIndex]; string token = rowTokens[colOffset + colIndex]; values_[LinearIndex(rowIndex, colIndex)] = colParser(token); } } }