11#include <unordered_map>
90 using ValueBase = std::variant<
116 using ValueBase::ValueBase;
119 : ValueBase( char_ptr_to_string_safe( arg ) )
124 using Row = std::unordered_map< std::string, Value >;
131 [[nodiscard]]
const std::vector< Row >&
rows()
const {
return rows_; }
135 [[nodiscard]]
const std::set< std::string >&
columns()
const {
return columns_; }
143 row[
"id"] = global_id_counter++;
145 columns_.insert(
"id" );
146 columns_.insert(
"timestamp" );
148 for (
const auto& [key, value] : row_data )
151 columns_.insert( key );
154 rows_.emplace_back( row );
164 for (
const auto& col : selected_columns )
166 result.columns_.insert( col );
169 for (
const auto& row : rows_ )
172 for (
const auto& col : result.columns_ )
176 result.rows_.push_back( std::move( new_row ) );
188 result.columns_ = columns_;
189 for (
const auto& row : rows_ )
191 if (
auto it = row.find( column );
192 it != row.end() && !std::holds_alternative< std::monostate >( it->second ) )
194 result.rows_.push_back( row );
207 result.columns_ = columns_;
208 for (
const auto& row : rows_ )
210 auto it = row.find( column );
211 if ( it != row.end() && it->second == value )
213 result.rows_.push_back( row );
226 result.columns_ = columns_;
227 for (
const auto& row : rows_ )
229 auto it = row.find( column );
230 if ( it != row.end() && predicate( it->second ) )
232 result.rows_.push_back( row );
245 template <
typename RawType >
248 std::vector< RawType > result;
250 for (
const auto& row : rows_ )
252 auto it = row.find( column );
253 if ( it == row.end() )
258 const auto& val = it->second;
262 using V = std::decay_t<
decltype( v ) >;
265 if constexpr ( std::is_same_v< V, RawType > )
267 result.push_back( v );
270 else if constexpr ( std::is_arithmetic_v< RawType > && std::is_arithmetic_v< V > )
272 result.push_back(
static_cast< RawType
>( v ) );
291 if ( columns_.empty() || rows_.empty() )
293 os <<
"Empty table.\n";
299 std::unordered_map< std::string, size_t > widths;
300 for (
const auto& col : columns_ )
302 widths[col] = col.size();
305 for (
const auto& row : rows_ )
307 for (
const auto& col : columns_ )
315 for (
const auto& col : columns_ )
317 os <<
"+" << std::string( widths[col] + 2,
'-' );
324 for (
const auto& col : columns_ )
326 os <<
" " << std::setw(
static_cast< int >( widths[col] ) ) << std::right << col <<
" |";
331 for (
const auto& row : rows_ )
334 for (
const auto& col : columns_ )
336 os <<
" " << std::setw(
static_cast< int >( widths[col] ) ) << std::right
364 for (
const auto& row : rows_ )
368 for (
const auto& [key, val] : row )
374 os <<
"\"" << key <<
"\":";
375 if ( std::holds_alternative< std::string >( val ) )
379 else if ( std::holds_alternative< std::monostate >( val ) )
383 else if ( std::holds_alternative< bool >( val ) )
385 os << ( std::get< bool >( val ) ?
"true" :
"false" );
408 print_header( os,
"," );
409 for (
const auto& row : rows_ )
412 for (
const auto& col : columns_ )
441 [](
const auto& val ) -> std::string {
442 using T = std::decay_t<
decltype( val ) >;
443 if constexpr ( std::is_same_v< T, std::monostate > )
447 else if constexpr ( std::is_same_v< T, std::string > )
451 else if constexpr ( std::is_same_v< T, bool > )
453 return val ?
"true" :
"false";
455 else if constexpr ( std::is_same_v< T, float > || std::is_same_v< T, double > )
457 std::ostringstream ss;
458 ss << std::scientific << std::setprecision( 3 ) << val;
463 return std::to_string( val );
475 auto it = row.find( col );
476 return ( it != row.end() ) ? it->second : std::monostate{};
480 std::vector< Row > rows_;
481 std::set< std::string > columns_;
483 inline static int global_id_counter = 0;
488 void print_header( std::ostream& os,
const std::string& sep )
const
491 for (
const auto& col : columns_ )
505 static std::string char_ptr_to_string_safe(
const char* val )
509 return std::string{};
525 auto trim = [](
const std::string& s ) {
527 while ( start < s.size() && std::isspace(
static_cast< unsigned char >( s[start] ) ) )
531 size_t end = s.size();
532 while ( end > start && std::isspace(
static_cast< unsigned char >( s[end - 1] ) ) )
536 return s.substr( start, end - start );
539 auto parse_line = [trim](
const std::string& line ) {
540 std::vector< std::string > result;
542 bool inQuotes =
false;
544 for (
size_t i = 0; i < line.size(); ++i )
546 if (
const char c = line[i]; c ==
'"' )
548 if ( inQuotes && i + 1 < line.size() && line[i + 1] ==
'"' )
550 field.push_back(
'"' );
555 inQuotes = !inQuotes;
558 else if ( c ==
',' && !inQuotes )
560 result.push_back( trim( field ) );
565 field.push_back( c );
569 result.push_back( trim( field ) );
573 auto infer_value = [](
const std::string& s ) ->
Table::Value {
574 if ( s ==
"true" || s ==
"TRUE" )
576 if ( s ==
"false" || s ==
"FALSE" )
582 long long iv = std::strtoll( s.c_str(), &end, 10 );
583 if ( end && *end ==
'\0' )
585 return static_cast< int64_t
>( iv );
589 double dv = std::strtod( s.c_str(), &end );
590 if ( end && *end ==
'\0' )
599 auto build_row = [infer_value](
600 const std::vector< std::string >& headers,
601 const std::vector< std::string >& fields ) ->
Table::Row {
603 for (
size_t i = 0; i < headers.size() && i < fields.size(); ++i )
605 row[headers[i]] = infer_value( fields[i] );
610 std::ifstream file( filename );
611 if ( !file.is_open() )
613 return {
"Could not open file: " + filename };
617 if ( !std::getline( file, line ) )
619 return {
"Could not find a single line of content in file: " + filename };
622 std::vector< std::string > headers = parse_line( line );
626 long line_number = 2;
627 while ( std::getline( file, line ) )
629 auto fields = parse_line( line );
631 if ( fields.size() != headers.size() )
634 "Error parsing CSV file (line " + std::to_string( line_number ) +
636 "Number of columns in row does not match number of headers.\n"
637 "Note that also empty lines could be the cause of this (an empty line has one column with empty value).\n"
642 auto row = build_row( headers, fields );
Table class for storing and manipulating tabular data.
Definition table.hpp:84
static constexpr int MAX_STRING_LENGTH
Max length of string values (required for safe reading of possibly non-null-terminated char arrays).
Definition table.hpp:87
const Table & print_jsonl(std::ostream &os=logroot) const
Print the table as JSON lines. Each row is a JSON object, one per line.
Definition table.hpp:360
static std::string value_to_string(const Value &v)
Convert a Value to a string for printing.
Definition table.hpp:438
void clear()
Clear all rows and columns from the table.
Definition table.hpp:429
Table query_rows_not_none(const std::string &column) const
Query rows where a column is not None.
Definition table.hpp:185
static Value get_value_from_row_or_none(const Row &row, const std::string &col)
Get a value from a row or return None if missing.
Definition table.hpp:473
Table query_rows_where(const std::string &column, const std::function< bool(const Value &) > &predicate) const
Query rows where a column satisfies a predicate.
Definition table.hpp:223
Table()=default
Construct an empty table.
const Table & print_csv(std::ostream &os=logroot) const
Print the table as CSV.
Definition table.hpp:404
Table select_columns(const std::vector< std::string > &selected_columns) const
Select a subset of columns from the table.
Definition table.hpp:160
std::unordered_map< std::string, Value > Row
Type for a table row (mapping column name to value).
Definition table.hpp:124
Table query_rows_equals(const std::string &column, const Value &value) const
Query rows where a column equals a value.
Definition table.hpp:204
void add_row(const Row &row_data)
Add a row to the table. Adds "id" and "timestamp" columns automatically.
Definition table.hpp:140
const Table & print_pretty(std::ostream &os=logroot) const
Print the table in a pretty formatted style.
Definition table.hpp:289
const std::vector< Row > & rows() const
Get all rows in the table.
Definition table.hpp:131
const std::set< std::string > & columns() const
Get all column names in the table.
Definition table.hpp:135
std::vector< RawType > column_as_vector(const std::string &column) const
Returns the values of a column in a vector.
Definition table.hpp:246
MPIRank rank()
Definition mpi.hpp:10
Result< Table > read_table_from_csv(const std::string &filename)
Attempts to read a csv file and converts that into a Table instance.
Definition table.hpp:523
std::string current_timestamp()
Get the current timestamp as a string.
Definition timestamp.hpp:10
detail::PrefixCout logroot([]() { return detail::log_prefix();})
std::ostream subclass that just logs on root and adds a timestamp for each line.
Type for table cell values.
Definition table.hpp:110
Value(const char *arg)
Definition table.hpp:118