C++ Pointers
STL Containers Containing STL Containers

Introduction

All the literature I referenced to learn how to use STL containers only showed how to use them to hold primitives, (e.g. integers, strings, etc.), and made no mention of how to handle anything more complex.

This C++ Pointer shows you how to work with a STL Vector or an STL Map stored in an STL Vector or an STL Map, which together form a simple means to handle application data. There are other STL containers, but once the use of these two containers is known the same techniques can be used for the others.

Overview of the Example Programs

To make the example relatable and easily understandable and focus on the code required for using the STL containers, rather than the application, the example code uses a simple data item of a colour code, that comprises the name of a colour and its Red, Green Blue colour values as a 24 bit number, stored using a std::string and an int. Consequently, the use cases are very simple, but make it easier to focus on how to use an STL container stored in an STL container.

I couldn't think of a simple use case of an object or struct stored in an STL container itself stored in an STL container, but combined with my C++ Pointer Objects, structs and STL Containers, hopefully you'll be able to implement an object or struct stored in an STL container in an STL container.

The program implements the same functionality for four example STL container combinations,

  1. an STL Map of STL Maps
  2. an STL Map of STL Vectors
  3. an STL Vector of STL Maps
  4. an STL Vector of STL Vectors

A container to hold the palettes of colour codes is created and passed by reference to an object of Loader. This loads the containers with palettes of colour code data, (colour name and 24 bit colour value), for three different colour palettes. To simplify the example, instead of using an input configuration file, the example input data is stored in a raw string literal, and loaded line by line, as if being read from a file, using a stringstream. The loader code contains examples of creating the contained container with data and then loading, copy creation into the containing container; or for cases where the contained data is intensive to copy, creating the contained data in the containing container directly.

The loaded data is then manipulated as follows,

  1. colour code data is written to a text file, demonstrating using iterators to iterate over the containers
  2. a colour code entry is modified
  3. a palette name is modified, only easily done when an STL Vector is used for the palette container
  4. colour code data is written to a text file, demonstrating using type deduction auto and range based loops

Within the Vectors, the colour code data is stored using a std::pair of std::string and an int, and the palette data using a std::pair of std::string and std::container. Using std::pair in the Vector effectively makes it a MultiMap, with search performance dependent on the performance of std::find_if etc., but with the,

This C++ Pointer assumes you know how to use a Vector or Map to hold primitives, as this is widely described in literature. If you want to know how to use the STL Vector or Map check out www.cppreference.com/cppstl.html or Google etc., for information on the Standard Template Library.

Please note that this C++ Pointer is distributed with
NO WARRANTY

Section 5 Disclaimer of Warranties and Limitation of Liability.

  1. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
  2. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
  3. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
.

How to Create a Map or Vector of Maps or Vectors

A Vector or Map of Vectors or Maps is created in the same way that a Vector or Map of primitives is created, shown as follows for the four combinations;

std::map<key type, std::map<sub-key type, value type>> containerName; std::map<key type, std::vector<std::pair<sub-key type, value type>>> containerName; std::vector<key type, std::map<sub-key type, value type>> containerName; std::vector<key type, std::vector<std::pair<sub-key type, value type>>> containerName;

How to Add an Element to an STL Container contained by an STL Container

When using primitive types, the examples use std::strings and ints, adding elements using emplace or insert is as normal. However, the procedure depends on whether we are adding a key, sub-key, value or just a sub-key, value to the current key.

How key, sub-key, value data is added depends on whether you have all the information at that instant, or if the sub-key, value data will be available later, and, or how performance intensive it is to copy construct the contained container. The following examples show how to add key, sub-key, value data when the sub-key, value data isn't available when the key is added, and, or if construction and subsequent copy construction of the sub-key, value container has a performance impact. See Loader.cpp for actual code.

Adding to a Map containing a Map

std::map<key type, std::map<sub-key type, value type>>::iterator iteratorName = containerName.begin(); … //inside data load code std::map<sub-key type, value type> blankEntry; auto insertedKey = containerName.emplace(key, blankEntry); iteratorName = insertedKey.first; iteratorName->second.clear(); // remove the blank entry … /* Add sub-keys and values as required. Make sure iteratorName still references the std::map<key, std::map<… that you want to add to! */ auto insertedSubKey = iteratorName->second.emplace( std::pair<sub-key type, value type>( subkey, value ));

Adding to a Map containing a Vector

std::map<key type, std::vector<std::pair<sub-key type, value type>>>::iterator iteratorName = containerName.begin(); … //inside data load code std::vector<std::pair<sub-key type, value type>> blankEntry; auto insertedKey = containerName.emplace(key, blankEntry); iteratorName = insertedKey.first; iteratorName->second.clear(); // remove the blank entry … /* Add sub-keys and values as required. Make sure iteratorName still references the std::map<key, std::vector<… that you want to add to! */ iteratorName->second.emplace_back(std::pair<sub-key type,value type>(subkey,value));

Adding to a Vector containing a Map

std::vector<std::pair<key type, std::map<sub-key type, value type>>> ::iterator iteratorName = containerName.begin(); … //inside data load code std::map<sub-key type, value type> blankEntry; containerName.emplace_back(key, blankEntry); iteratorName = std::prev(containerName.end(), 1); iteratorName->second.clear(); // remove the blank entry … /* Add sub-keys and values as required. Make sure iteratorName still references the std::vector<key, std::map<… that you want to add to! */ iteratorName->second.emplace(std::pair<sub-key type,value type>(subkey,value));

Adding to a Vector containing a Vector

std::vector<std::pair<key type, std::vector<std::pair<sub-key type, value type>>>::iterator iteratorName = containerName.begin(); … //inside data load code std::vector<std::pair<sub-key type, value type>> blankEntry; containerName.emplace_back(key, blankEntry); iteratorName = std::prev(containerName.end(), 1); iteratorName->second.clear(); // remove the blank entry … /* Add sub-keys and values as required. Make sure iteratorName still references the std::vector<key, std::vector<… that you want to add to! */ iteratorName->second.emplace_back(std::pair<sub-key type,value type>(subkey,value));

Note, the above examples for .emplace-ing into a Map, do not show checking the returned iterator for, and handling, a failure to insert the key. You can also use insert. For Vectors, you can use push_back.

Following are an example of adding a key, sub-key, value into a Map and into a Vector in one go. For the examples I have created, the key and the sub-key, value data are read by consecutive reads, there is an application problem that requires extra book-keeping logic to ensure that a partially created key, sub-key, value data item doesn't get missed, see Loader.cpp for more details.

Adding to a Map containing a Vector

//inside data load code std::vector<std::pair<sub-key type, value type>> firstEntry {std::pair<sub-key type, value type(subKey, value)}; auto insertedKey = containerName.emplace(key, firstEntry); // subsequent subKey, value data added the same as the method above. See Loader.cpp

Adding to a Vector containing a Map

//inside data load code std::map<sub-key type, value type>> firstEntry {std::pair<sub-key type, value type(subKey, value)}; containerName.emplace_back(key, firstEntry); // subsequent subKey, value data added the same as the method above. See Loader.cpp

How to Create an Iterator for STL Container containing an STL Container

See examples above. The addition of type deduction, (auto) to the language simplifies iterator creation.

How to Find data in an STL Contained in an STL Container

Use the appropriate find method for the containing STL container to find the STL container associated with key, ( e.g. map.find(), std::find_if(…) for vector, etc. ). This will return an iterator of std::pair<key, STL container>. Then use the appropriate find method for the contained STL container to find the element associated with the sub key for STL Container->second

Software for Download and Installation

This C++ Pointer was compiled and tested using Apple clang version 14.0.0 on x86_64-apple-darwin22.3.0 and with Windows 10 using cygwin-3.2.0, gcc version 10.2.0

To obtain the source code, install and execute this C++ Pointer use the following procedure.

Description
Shell Commands
Download TSSCppPtr3.tar.gz and save to a folder
 
Extract TSSCppPtr3.tar.gz
Open a terminal window, navigate to the folder and expand the file using the command
$ tar -xvzf TSSCppPtr3.tar.gz
This will create the folder TSSCppPtr3 and place the source files in it
 
Go to the TSSCppPtr3 folder
$ cd TSSCppPtr3
Use your favourite text editor to view the files TSSCpppPtr3.cpp and Loader.cpp which contain the code demonstrating using a Vector and a Map in a Vector and a Map vice versa
 
Optionally
Build the executable
$ make
Run the example
$ ./TSSCppPtr3
Use your favourite text editor to view the result file
 

Revision History

Rev
Date
Details
0.0
18-Apr-2023
Initial version

Technical Support

Bug-Reports

If you suspect a bug please e-mail a description being sure to identify that the bug report is about C++ Pointer #3 and include as much detail as possible.