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 <vector>
using namespace std;
Note the inclusion of the vector library.
Article Content
- Construction
- Access with Indexes
- Accessing in Sequence
- Inserting a Row
- Appending a Row
- Erasing Rows
- Clear
- Conclusion
Construction
The construction of a normal vector begins with:
Name is the name of the vector. The following code creates a one-dimensional vector with a braced initialization list of 5 characters:
To construct a vector of vectors, begin with:
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.
{'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<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:
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):
{'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:
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:
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
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:
{'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:
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:
{'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
The iterator was incremented twice to point to the third row before insertion. The insertion statement could equally have been written as,
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:
{'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
* $ % $ &
The push_back() statement could equally have been written as,
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:
{'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
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:
{'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].