2

I've read plenty of SO threads on vector to array conversion, but how would a vector<vector<?>> be converted to a singly dimensioned array? I recently discovered the vector's data function; could that be used somehow?

Community
  • 1
  • 1
T145
  • 1,415
  • 1
  • 13
  • 33
  • You can use `data()` to get the internal array. Check out how the array looks like by `cout`-ing it. Bear in mind that this kind of thing is probably platform specific. If you want a specific layout, and you know what type the inner vector holds (i.e if it doesn't hold another vector that holds another vector, etc.) then you can write your own loop for that. –  Apr 13 '15 at 15:06

2 Answers2

4

You're on the right track with the .data() member function, but that would give you an array of objects of type std::vector<T>, not an array of objects of type T. To truly flatten a nested vector you will need to do it yourself. Something like this would probably do the trick.

// 1. Compute the total size required.
int total_size = 0;
for (auto& vec : vectors) total_size += vec.size();

// 2. Create a vector to hold the data.
std::vector<T> flattened;
flattened.reserve(total_size);

// 3. Fill it
for (auto& vec : vectors)
    for (auto& elem : vec)
        flattened.push_back(elem);

// 4. Obtain the array
auto ptr = flattened.data();

For older compilers, you can iterate through the vectors like so

for (std::vector<std::vector<T> >::iterator iter = vectors.begin();
     iter != vectors.end(); ++iter) {
    for (std::vector<T>::iterator iter2 = iter->begin();
         iter2 != iter->end(); ++iter2) {
        flattened.push_back(*iter2);
    }
}

Or just use plain old indices and the .size() member function.

Internally, std::vector holds on to a pointer to its elements, and so the outermost data() must conceptually be treated like an array of pointers, not a 2D array. Therefore we have to manually walk through and flatten it.

bstamour
  • 7,746
  • 1
  • 26
  • 39
  • If I were to not use the range-based for, what would be the alternative for older compilers? I keep getting conversion errors. My variable is `vector > matrix(n, vector (n, 0));`, where `n` is any positive number, and my iterator is declared as `for (vector::iterator vec = matrix.begin(); vec < matrix.end(); vec++)`. – T145 Apr 13 '15 at 17:37
  • See my edit. you need to use `vector >::iterator` for the outermost loop, and `vector::iterator` for the innermost. – bstamour Apr 14 '15 at 13:11
1

To illustrate how nested vectors are stored and show why it is hard to convert then into arrays I did the following experiment (with results):

// Libraries
    #include <iostream>
    #include <vector>

// Namespaces
  using namespace std;

int main()
{
    // Create a two dimensional vector containing integers
    vector<vector<int>> v = {{11, 21, 31, 41},{12, 22, 32, 42},{13, 23, 33, 43},{14, 24, 34, 44}};

    // Add more integers to vector
    v.push_back({15, 25, 35, 45});
    v.push_back({16});

    // Iterate and print values of vector in standard way
    cout << "This is the vector:\n  ";
    for(auto v_row = v.begin(); v_row != v.end(); v_row++) {
        for ( auto v_element = v_row->begin(); v_element != v_row->end(); v_element++) {
            cout << *v_element << " ";
        }
        cout << "\n  ";
    }

    // create pointer to the first element in the vector
    int *a = &v[0][0];

    cout << "\nReference memory pointer (vector element[0][0]): " << a << "\n" ;

    // get the vector size
    int max_row = v.size();
    int max_col = v[0].size(); // taking the first row as reference

    // Iterate and print relative memory address position of each element on vector
    cout << "\nRelative memory addresses:\n  ";;
    for(int a_row = 0; a_row < max_row; a_row++) {
        for ( int a_col = 0; a_col < max_col; a_col++) {
            cout << &v[a_row][a_col] - a << " ";
        }
        cout << "\n  ";
    }

    // Iterate and print values memory, starting on the first vector element.
    cout << "\nThis is the content of the memory block:\n  ";
    for(int a_row = 0; a_row < max_row; a_row++) {
        for ( int a_col = 0; a_col < max_col; a_col++) {
            cout << a[(a_row*max_row) + a_col] << " ";
        }
        cout << "\n  ";
    }
    cout << "\n";
}

The result was:

This is the vector:
  11 21 31 41
  12 22 32 42
  13 23 33 43
  14 24 34 44
  15 25 35 45
  16

Reference memory pointer (vector element[0][0]): 0x606100

Relative memory addresses:
  0 1 2 3
  8 9 10 11
  16 17 18 19
  24 25 26 27
  -60 -59 -58 -57
  -52 -51 -50 -49

This is the content of the memory block:
  11 21 31 41
  33 0 12 22
  0 0 33 0
  33 43 0 0
  14 24 34 44
  209 0 6316288 0

In my case, I need the data to be in a continuous memory block, such as to enable the use of HDF5 to export the two dimensional dataset into files.

In the end it became more efficient to move back to old fashioned arrays from the start in order to avoid the need of performing memory copies or flattening operations, such as proposed. Such operations seems to be the only alternative but take a lot of resources, specially when handling very large arrays.

Fábio Lobão
  • 171
  • 1
  • 11
  • Correcting my previous statement: Even when a multidimensional memory continuous data set is needed, it is preferable to use a 1D vector and handle the indexing outside instead of using old fashion arrays due to the chance of unwanted memory leaks and problem with stack and heap memory. – Fábio Lobão Jun 07 '19 at 12:46