Vince's CSV Parser
Loading...
Searching...
No Matches
data_frame_row.hpp
1#pragma once
2
3#include <stdexcept>
4#include <string>
5#include <vector>
6
7#include "../csv_exceptions.hpp"
8#include "../csv_row.hpp"
9#include "../json_converter.hpp"
10#include "data_frame_cell.hpp"
11#include "fwd.hpp"
12
13namespace csv {
14 template<typename KeyType>
16 public:
18 DataFrameRow() : row(nullptr), frame(nullptr), row_index(0), row_overlay(nullptr), key_ptr(nullptr), can_mutate(false) {}
19
22 const CSVRow* _row,
23 DataFrame<KeyType>* _frame,
24 size_t _row_index,
25 RowOverlay* _edits,
26 const KeyType* _key
27 ) : row(_row), frame(_frame), row_index(_row_index), row_overlay(_edits), key_ptr(_key), can_mutate(true) {}
28
31 const CSVRow* _row,
32 const DataFrame<KeyType>* _frame,
33 size_t _row_index,
34 const RowOverlay* _edits,
35 const KeyType* _key
36 ) : row(_row), frame(_frame), row_index(_row_index), row_overlay(_edits), key_ptr(_key), can_mutate(false) {}
37
39#if CSV_EXCEPTIONS_ENABLED
40 DataFrameCell operator[](const std::string& col) {
41 return field_or_throw(*this, col);
42 }
43#endif
44
46#if CSV_EXCEPTIONS_ENABLED
48 return field_or_throw(*this, n);
49 }
50#endif
51
53#if CSV_EXCEPTIONS_ENABLED
54 DataFrameCell operator[](const std::string& col) const {
55 return field_or_throw(*this, col);
56 }
57#endif
58
60#if CSV_EXCEPTIONS_ENABLED
61 DataFrameCell operator[](size_t n) const {
62 return field_or_throw(*this, n);
63 }
64#endif
65
67 DataFrameCell try_field(size_t index, CSVError& error) {
68 if (!this->validate_field(index, error)) return {};
69 return frame ? frame->try_make_cell(row_index, index, can_mutate, error) : this->make_cell(index);
70 }
71 DataFrameCell try_field(size_t index, CSVError& error) const {
72 if (!this->validate_field(index, error)) return {};
73 return frame ? frame->try_make_cell(row_index, index, false, error) : this->make_cell(index);
74 }
75 DataFrameCell try_field(const std::string& name, CSVError& error) {
76 const size_t index = this->try_find_column(name, error);
77 return error ? DataFrameCell() : this->try_field(index, error);
78 }
79 DataFrameCell try_field(const std::string& name, CSVError& error) const {
80 const size_t index = this->try_find_column(name, error);
81 return error ? DataFrameCell() : this->try_field(index, error);
82 }
83
84 bool try_erase(CSVError& error) {
85 error.clear();
86 if (!can_mutate || !frame)
87 return internals::fail_static(error, CSVErrorCode::ReadOnlyProxy, internals::ERROR_CANNOT_ERASE_CONST_DF_ROW);
88 return const_cast<DataFrame<KeyType>*>(frame)->erase_at_index(row_index);
89 }
90
92 bool is_valid() const noexcept { return row != nullptr; }
93
95 size_t size() const { return frame ? frame->n_cols() : (row ? row->size() : 0); }
96
98 bool empty() const { return this->size() == 0; }
99
101 const std::vector<std::string>& get_col_names() const { return frame ? frame->columns() : row->get_col_names(); }
102
106 const CSVRow& get_underlying_row() const { return *row; }
107
109 const KeyType& key() const { return *key_ptr; }
110
115#if CSV_EXCEPTIONS_ENABLED
116 bool erase() {
117 CSVError error;
118 const bool result = this->try_erase(error);
119 internals::throw_dataframe_error(error);
120 return result;
121 }
122#endif
123
125#if CSV_EXCEPTIONS_ENABLED
126 operator std::vector<std::string>() const {
127 std::vector<std::string> result;
128 result.reserve(this->size());
129
130 for (size_t i = 0; i < this->size(); i++) {
131 result.push_back(std::string(this->make_cell(i).get_sv()));
132 }
133 return result;
134 }
135#endif
136
139#if CSV_EXCEPTIONS_ENABLED
140 std::string to_json(const std::vector<std::string>& subset = {}) const {
141 CSVError error;
142 auto result = this->try_to_json(subset, error);
143 // JSON subset lookups historically use runtime_error.
144 internals::throw_error(error);
145 return result;
146 }
147#endif
148
150 std::string try_to_json(const std::vector<std::string>& subset, CSVError& error) const {
151 if (!this->validate_fields(subset, error)) return {};
152 const field_string_accessor field_at(this);
153 return frame ? this->get_frame_json_converter().try_row_to_json(this->size(), field_at, subset, error)
154 : this->make_detached_json_converter().try_row_to_json(this->size(), field_at, subset, error);
155 }
156
159#if CSV_EXCEPTIONS_ENABLED
160 std::string to_json_array(const std::vector<std::string>& subset = {}) const {
161 CSVError error;
162 auto result = this->try_to_json_array(subset, error);
163 // JSON subset lookups historically use runtime_error.
164 internals::throw_error(error);
165 return result;
166 }
167#endif
168
170 std::string try_to_json_array(const std::vector<std::string>& subset, CSVError& error) const {
171 if (!this->validate_fields(subset, error)) return {};
172 const field_string_accessor field_at(this);
173 return frame ? this->get_frame_json_converter().try_row_to_json_array(this->size(), field_at, subset, error)
174 : this->make_detached_json_converter().try_row_to_json_array(this->size(), field_at, subset, error);
175 }
176
177#if defined(CSV_HAS_CXX20) && CSV_EXCEPTIONS_ENABLED
183 auto to_sv_range() const {
184 return std::views::iota(size_t{0}, this->size())
185 | std::views::transform([this](size_t i) { return std::string(this->make_cell(i).get_sv()); });
186 }
187#endif
188
189 private:
190#if CSV_EXCEPTIONS_ENABLED
191 // Preserve the proxy's constness when selecting checked access.
192 template<typename Self, typename Column>
193 static DataFrameCell field_or_throw(Self& self, const Column& column) {
194 CSVError error;
195 auto result = self.try_field(column, error);
196 internals::throw_dataframe_error(error);
197 return result;
198 }
199#endif
200
201 bool validate_field(size_t index, CSVError& error) const {
202 error.clear();
203 if (!row)
204 return internals::fail_static(error, CSVErrorCode::InvalidProxy, "DataFrameRow proxy is unbound");
205 if (index >= this->size())
206 return internals::fail_static(error, CSVErrorCode::IndexOutOfBounds, internals::ERROR_COLUMN_INDEX_OUT_OF_RANGE);
207 return true;
208 }
209
210 struct field_string_accessor {
211 explicit field_string_accessor(const DataFrameRow* owner) : owner(owner) {}
212
213 std::string operator()(size_t i) const {
214 return std::string(owner->make_cell(i).get_sv());
215 }
216
217 const DataFrameRow* owner;
218 };
219
220 const internals::JsonConverter& get_frame_json_converter() const {
221 return frame->json_converter_.get_or_create([this]() {
222 return std::make_shared<internals::JsonConverter>(frame->columns());
223 });
224 }
225
226 internals::JsonConverter make_detached_json_converter() const {
227 return internals::JsonConverter(this->get_col_names());
228 }
229
230 DataFrameCell make_cell(size_t col_index) {
231 if (frame) {
232 return frame->make_cell(row, row_index, col_index, can_mutate);
233 }
234
235 return can_mutate
236 ? DataFrameCell(row, const_cast<RowOverlay*>(row_overlay), col_index)
237 : DataFrameCell(row, row_overlay, col_index);
238 }
239
240 DataFrameCell make_cell(size_t col_index) const {
241 if (frame) {
242 return frame->make_cell(row, row_index, col_index, false);
243 }
244
245 return DataFrameCell(row, row_overlay, col_index);
246 }
247
248 size_t try_find_column(const std::string& col, CSVError& error) const {
249 error.clear();
250 if (!row) {
251 internals::fail_static(error, CSVErrorCode::InvalidProxy, "DataFrameRow proxy is unbound");
252 return 0;
253 }
254 if (frame) return frame->try_find_column(col, error);
255 const int position = row->col_names_ptr()->index_of(col);
256 if (position == CSV_NOT_FOUND) {
257 internals::fail(error, CSVErrorCode::ColumnNotFound, std::string(internals::ERROR_COLUMN_NOT_FOUND) + col);
258 return 0;
259 }
260 return static_cast<size_t>(position);
261 }
262
263 bool validate_fields(const std::vector<std::string>& subset, CSVError& error) const {
264 error.clear();
265 if (subset.empty()) {
266 for (size_t i = 0; i < this->size(); ++i) {
267 this->try_field(i, error);
268 if (error) return false;
269 }
270 } else {
271 for (const auto& name : subset) {
272 this->try_field(name, error);
273 if (error) return false;
274 }
275 }
276 return row || internals::fail_static(error, CSVErrorCode::InvalidProxy, "DataFrameRow proxy is unbound");
277 }
278
279 const CSVRow* row;
280 const DataFrame<KeyType>* frame;
281 size_t row_index;
282 const RowOverlay* row_overlay;
283 const KeyType* key_ptr;
284 bool can_mutate;
285 };
286
287}
Data structure for representing CSV rows.
Definition csv_row.hpp:533
const std::vector< std::string > & get_col_names() const
Retrieve this row's associated column names.
Definition csv_row.hpp:619
CONSTEXPR size_t size() const noexcept
Return the number of fields in this row.
Definition csv_row.hpp:555
internals::ConstColNamesPtr col_names_ptr() const noexcept
Internal accessor for preserving resolved column-name lookup policy across helper types.
Definition csv_row.hpp:624
DataFrameCell operator[](const std::string &col)
Access a field by column name, preserving edit support.
std::string to_json(const std::vector< std::string > &subset={}) const
Convert to JSON.
DataFrameRow(const CSVRow *_row, const DataFrame< KeyType > *_frame, size_t _row_index, const RowOverlay *_edits, const KeyType *_key)
Construct a read-only DataFrameRow wrapper.
auto to_sv_range() const
Convert this DataFrameRow into a std::ranges::input_range of strings, respecting the sparse overlay (...
DataFrameRow(const CSVRow *_row, DataFrame< KeyType > *_frame, size_t _row_index, RowOverlay *_edits, const KeyType *_key)
Construct a mutable DataFrameRow wrapper.
DataFrameCell operator[](size_t n)
Access a field by position, preserving edit support.
DataFrameRow()
Default constructor (creates an unbound proxy).
const std::vector< std::string > & get_col_names() const
Get column names.
DataFrameCell operator[](size_t n) const
Access a field by position, checking edits first.
DataFrameCell operator[](const std::string &col) const
Access a field by column name, checking edits first.
std::string to_json_array(const std::vector< std::string > &subset={}) const
Convert to JSON array.
size_t size() const
Get the number of visible fields in the row.
std::string try_to_json_array(const std::vector< std::string > &subset, CSVError &error) const
Serialize selected visible columns, reporting lookup and physical-row bounds errors.
std::string try_to_json(const std::vector< std::string > &subset, CSVError &error) const
Serialize selected visible columns, reporting lookup and physical-row bounds errors.
const KeyType & key() const
Get the key for this row (only valid for keyed DataFrames).
bool empty() const
Check if the row is empty.
const CSVRow & get_underlying_row() const
Get the underlying CSVRow without applying cell edits or column visibility.
bool is_valid() const noexcept
Whether the proxy is bound.
bool erase()
Delete this row from the parent DataFrame.
DataFrameCell try_field(size_t index, CSVError &error)
Checked cell access.
size_t n_cols() const noexcept
Get the number of visible columns in the DataFrame.
const std::vector< std::string > & columns() const noexcept
Get the column names in order.
The all encompassing namespace.
@ IndexOutOfBounds
A row/column position exceeds the visible schema or physical record width.
@ InvalidProxy
A proxy is unbound or a column proxy was invalidated by structural mutation.
@ ColumnNotFound
A requested name is absent from the visible column schema.
@ ReadOnlyProxy
Mutation was requested through a cell, row, or column without edit permission.
constexpr int CSV_NOT_FOUND
Integer indicating a requested column wasn't found.
Definition common.hpp:494
A diagnostic whose text remains valid for the lifetime of the error.
Definition csv_error.hpp:94