Example #1
0
        /// <summary>
        /// Convert to a multi-line CSV text with column and/or row headers.
        ///
        /// Escapes separator with quotes if necessary.
        /// </summary>
        public override string ToString()
        {
            StringBuilder result = new StringBuilder();

            // Validate table before serializing it to CSV string
            Validate();

            // Add column headers, if any
            if (ColHeaders != null)
            {
                IEnumerable <string> firstRowTokens = null;
                if (RowHeaders != null)
                {
                    // Prepend corner value if row headers are also present
                    firstRowTokens = new string[1] {
                        CornerHeader
                    }.Concat(ColHeaders);
                }
                else
                {
                    // No corner value
                    firstRowTokens = ColHeaders;
                }
                string firstRowString = CsvUtil.TokensToLine(firstRowTokens);
                result.AppendLine(firstRowString);
            }

            int colOffset = RowHeaders != null ? 1 : 0;

            for (int rowIndex = 0; rowIndex < RowCount; ++rowIndex)
            {
                string[] tokens = new string[colOffset + ColCount];

                if (colOffset != 0)
                {
                    // Add row header, if present
                    tokens[0] = RowHeaders[rowIndex];
                }

                for (int colIndex = 0; colIndex < ColCount; ++colIndex)
                {
                    // Add values with offset for row header, if present
                    tokens[colOffset + colIndex] = this[rowIndex, colIndex].AsString();
                }

                // Convert to CSV and append
                string csvLine = CsvUtil.TokensToLine(tokens);
                result.AppendLine(csvLine);
            }

            return(result.ToString());
        }
Example #2
0
        /// <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);
                }
            }
        }