C++

Can You Make a Vector of Vectors in C++?

Yes! Yes, you can make a vector of vectors in C++. The normal vector is a one-dimensional list data structure. A vector of vectors is a two-dimensional list data structure, from two normal vectors. A 2-dimensional list is a table, without a proper header row and without a proper header column. A vector of vectors is one vector nesting other vectors. The template argument for the outer vector, is a vector. And so, a vector of vectors can only be of one type, e.g., all integers or all characters.

This article explains how to create a vector of vectors and how to apply some obvious member functions of the vector, to vector of vectors. In order to do this, the C++ program should begin with:

#include <iostream>
  #include <vector>
  using namespace std;

Note the inclusion of the vector library.

Article Content

Construction

The construction of a normal vector begins with:

vector<type> name

Name is the name of the vector. The following code creates a one-dimensional vector with a braced initialization list of 5 characters:

vector<char> vtr = {'A', 'B', 'C', 'D', 'E'};

To construct a vector of vectors, begin with:

vector<vector<type>> name

Notice how a vector template has become another template argument. So, it should be interpreted as vector of vectors of the same type. Name is the name of the vector of vectors. The following code creates a two-dimensional vector with 6 braced initialization lists of 5 characters each for 6 rows.

vector<vector<char>> vtr = {{'A', 'B', 'C', 'D', 'E'},

    {'A', 'B', 'C', 'D', 'E'},

    {'A', 'B', 'C', 'D', 'E'},

    {'A', 'B', 'C', 'D', 'E'},

    {'A', 'B', 'C', 'D', 'E'},

    {'A', 'B', 'C', 'D', 'E'}};

The outer vector literal, has delimiters, { and }. Each vector for a row has delimiters, { and }. The row vector literals are separated by commas. The ending semicolon is at the bottom right end of the creating table. The 2D vector could have as well been created as follows:

vector<char> oneDV = {'A', 'B', 'C', 'D', 'E'};
vector<vector<char>> twoDV = {oneDV, oneDV, oneDV, oneDV, oneDV, oneDV};

That is, a 1D vector is created, and its variable name is used as identifiers for the different rows.

Now, the rows can actually have different contents. In that case, each row will be a different vector with a different name.

Access with Indexes

The syntax to access an element is:

2DvectorName[i][j]

Where i is the variable for a particular row, and j is the variable for a particular column. Row counting begins from zero, and column counting also begins from zero. The two-dimensional vector of vectors does not have to be regular; that is, the number of columns for each row do not have to be the same. The following code reads the value of index row 2 (third row) and index column 3 (fourth column):

vector<vector<char>> vtr = {{'A', 'B', 'C', 'D', 'E'},

  {'A', 'B', 'C', 'D', 'E'},

  {'A', 'B', 'C', 'D', 'E'},

  {'A', 'B', 'C', 'D', 'E'},

  {'A', 'B', 'C', 'D', 'E'},

  {'A', 'B', 'C', 'D', 'E'}};

 char ch = vtr[2][3];

cout << ch << endl;

The output is, ‘D’.

This can be changed and read again in the same way, with the following code segment:

vtr[2][3] = 'Z';

  char ch = vtr[2][3];

cout << ch << endl;

In this case, the output is ‘Z’.

Accessing in Sequence

The first row can be accessed beginning from the first element, then the second element, then the third element, until the last element of the first row. Then, the next row can be accessed in the same way, then the one after, and then the one following, until the last row is completed. This needs two for-loops as the following code illustrates:

    vectoroneDV = {'A', 'B', 'C', 'D', 'E'};
    vector<vector>twoDV = {oneDV, oneDV, oneDV, oneDV, oneDV, oneDV};

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is:

A B C D E

A B C D E

A B C D E

A B C D E

A B C D E

A B C D E

Note that twoDV.size() gives the number of rows for the whole table, while twoDV[i].size() gives the number of cells (columns) for a particular row.

Inserting a Row

Inserting in Front

A row is to a 2D vector, as a cell is to a 1D vector. The same insertion approach is used, but instead of a cell literal, a row literal is used; instead of a value identifier, a row identifier (e.g., twoDV[i]) is used. The following code shows how a row is inserted in front of the 2D vector:

vector<vector>twoDV = {{'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},                            
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'}};

    vector<vector>::iterator p = twoDV.begin();

    vectoroneDV = {'*', '$', '%', '$', '&'};

twoDV.insert(p, oneDV);

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is:

* $ % $ &amp;

A B C D E

A B C D E

A B C D E

A B C D E

The begin() member function returns an iterator that points to the first row of the 2D vector. Note that the iterator returned has to be of type vector of vectors (e.g. vector<vector<char>>::iterator p). Insertion takes place in front of where the iterator is pointing.

Inserting Within

The following code inserts a row within the table, in front of the pointed third row:

vector<vector>twoDV = {{'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},                            
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'}};

    vector<vector>::iterator p = twoDV.begin();
    p++; p++;

    vectoroneDV = {'*', '$', '%', '$', '&'};

twoDV.insert(p, oneDV);

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is:

A B C D E

A B C D E

* $ % $ &

A B C D E

A B C D E

The iterator was incremented twice to point to the third row before insertion. The insertion statement could equally have been written as,

twoDV.insert(p, {'*', '$', '%', '$', '&'});

The result would have been the same.

Appending a Row

A row can be appended using the push_back() one dimensional function. The following code illustrates this:

vector<vector>twoDV = {{'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},                            
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'}};

    vectoroneDV = {'*', '$', '%', '$', '&'};

twoDV.push_back(oneDV);

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is:

A B C D E

A B C D E

A B C D E

A B C D E

* $ % $ &

The push_back() statement could equally have been written as,

twoDV.push_back({'*', '$', '%', '$', '&'});

The result would have been the same.

Erasing Rows

The following code uses the one-dimensional erase() vector member function to erase the second and third rows, though the second iterator is pointing to the fourth row, of the 5 row vector of vectors:

vector<vector>twoDV = {{'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},                            
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'}};

    vector<vector>::iterator p = twoDV.begin();
    p++;
    vector<vector>::iterator q = twoDV.end();
    q--; q--;

twoDV.erase(p, q);

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is:

A B C D E

A B C D E

A B C D E

The one-dimensional end() vector member function returns an iterator, which is pointing just after the end of the one-dimensional vector (which is now a vector of vectors). It is decremented twice in the above code, to point to the last-but-one row. Whenever a range of elements or rows are to be erased, the element or row pointed to by the second iterator, is not erased.

Clear

An element is to a one-dimensional vector, as a row is to a two-dimensional vector (vector of vectors). All the rows of a vector can be erased with the one-dimensional clear() member function. The following code illustrates this:

vector<vector>twoDV = {{'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},                            
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'},
                                  {'A', 'B', 'C', 'D', 'E'}};
twoDV.clear();

    for (int i=0; i<twoDV.size(); i++) {
        for (int j=0; j<twoDV[i].size(); j++) {
cout<<twoDV[i][j] << ' ';
        }
cout<<endl;
    }
cout<<endl;

The output is nothing.

Conclusion

An element is to a one-dimensional vector, as a row is to a two-dimensional vector (vector or vectors). All the one-dimensional member functions for the one-dimensional vector, can be used for the two-dimensional vector, addressing the rows instead of the elements. Individual cells of the table can be accessed with twoDV[i][j], where twoDV, i and j have their common meanings. The vector of rows can be addressed with twoDV, and each row can be addressed with twoDV[i].

About the author

Chrysanthus Forcha

Discoverer of mathematics Integration from First Principles and related series. Master’s Degree in Technical Education, specializing in Electronics and Computer Software. BSc Electronics. I also have knowledge and experience at the Master’s level in Computing and Telecommunications. Out of 20,000 writers, I was the 37th best writer at devarticles.com. I have been working in these fields for more than 10 years.