Transforming a vector of structs into a columnar Table. The final representation should be an `arrow::Table` which in turn is made up of an `arrow::Schema` and a list of `arrow::ChunkedArray` instances. As the first step, we will iterate over the data and build up the arrays incrementally. For this task, we provide `arrow::ArrayBuilder` classes that help in the construction of the final `arrow::
| 58 | // `arrow::DoubleBuilder` that constructs the underlying values array that |
| 59 | // is referenced by the offsets in the former array. |
| 60 | arrow::Result<std::shared_ptr<arrow::Table>> VectorToColumnarTable( |
| 61 | const std::vector<struct data_row>& rows) { |
| 62 | // The builders are more efficient using |
| 63 | // arrow::jemalloc::MemoryPool::default_pool() as this can increase the size of |
| 64 | // the underlying memory regions in-place. At the moment, arrow::jemalloc is only |
| 65 | // supported on Unix systems, not Windows. |
| 66 | arrow::MemoryPool* pool = arrow::default_memory_pool(); |
| 67 | |
| 68 | Int64Builder id_builder(pool); |
| 69 | Int64Builder components_builder(pool); |
| 70 | ListBuilder component_cost_builder(pool, std::make_shared<DoubleBuilder>(pool)); |
| 71 | // The following builder is owned by component_cost_builder. |
| 72 | DoubleBuilder* component_item_cost_builder = |
| 73 | (static_cast<DoubleBuilder*>(component_cost_builder.value_builder())); |
| 74 | |
| 75 | // Now we can loop over our existing data and insert it into the builders. The |
| 76 | // `Append` calls here may fail (e.g. we cannot allocate enough additional memory). |
| 77 | // Thus we need to check their return values. For more information on these values, |
| 78 | // check the documentation about `arrow::Status`. |
| 79 | for (const data_row& row : rows) { |
| 80 | ARROW_RETURN_NOT_OK(id_builder.Append(row.id)); |
| 81 | ARROW_RETURN_NOT_OK(components_builder.Append(row.components)); |
| 82 | |
| 83 | // Indicate the start of a new list row. This will memorise the current |
| 84 | // offset in the values builder. |
| 85 | ARROW_RETURN_NOT_OK(component_cost_builder.Append()); |
| 86 | // Store the actual values. The same memory layout is |
| 87 | // used for the component cost data, in this case a vector of |
| 88 | // type double, as for the memory that Arrow uses to hold this |
| 89 | // data and will be created. |
| 90 | ARROW_RETURN_NOT_OK(component_item_cost_builder->AppendValues( |
| 91 | row.component_cost.data(), row.component_cost.size())); |
| 92 | } |
| 93 | |
| 94 | // At the end, we finalise the arrays, declare the (type) schema and combine them |
| 95 | // into a single `arrow::Table`: |
| 96 | std::shared_ptr<arrow::Array> id_array; |
| 97 | ARROW_RETURN_NOT_OK(id_builder.Finish(&id_array)); |
| 98 | std::shared_ptr<arrow::Array> components_array; |
| 99 | ARROW_RETURN_NOT_OK(components_builder.Finish(&components_array)); |
| 100 | // No need to invoke component_item_cost_builder.Finish because it is implied by |
| 101 | // the parent builder's Finish invocation. |
| 102 | std::shared_ptr<arrow::Array> component_cost_array; |
| 103 | ARROW_RETURN_NOT_OK(component_cost_builder.Finish(&component_cost_array)); |
| 104 | |
| 105 | std::vector<std::shared_ptr<arrow::Field>> schema_vector = { |
| 106 | arrow::field("id", arrow::int64()), arrow::field("components", arrow::int64()), |
| 107 | arrow::field("component_cost", arrow::list(arrow::float64()))}; |
| 108 | |
| 109 | auto schema = std::make_shared<arrow::Schema>(schema_vector); |
| 110 | |
| 111 | // The final `table` variable is the one we can then pass on to other functions |
| 112 | // that can consume Apache Arrow memory structures. This object has ownership of |
| 113 | // all referenced data, thus we don't have to care about undefined references once |
| 114 | // we leave the scope of the function building the table and its underlying arrays. |
| 115 | std::shared_ptr<arrow::Table> table = |
| 116 | arrow::Table::Make(schema, {id_array, components_array, component_cost_array}); |
| 117 |
no test coverage detected