Best practices with 3D vectors

Hello everyone. Not long ago, I created a thread to help better understand the use of 2D vectors. With that out of the way, I took it to the next step and populated a 3D vector by clearing the 2D vector after reading in each block of data.

It's a simple program, that reads in addresses from a text file (each address has a blank line in between it), and can store them within 2d vectors, regardless of how many words on any given line, or how many lines within any given address. From there, it populates a 3d vector much like pages in an address book. Because not all lines or pages have the same length, I think of it as an address book where some pages may have rips on the edges as an appropriate analogy.

The program works although it fails to store the last address, and I was able to figure out this adaptation from the single 2d vector used previously on my own. However, I understand looping based on vector sizes in this manner is bad practice, and wondered how I could make my triple output loop more like the one that is commented out. In the next post, I've already figured that out, but how can I get it to store the last address when reaching the end of the file the way it stores the others?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;

int main()
{
    string line;//string used to read in each line from file
    vector<vector<string> > addresses;//2D vector used to store addresses in grid format
    vector<vector<vector<string> > > book;//3D vector used to store addresses like pages
    
    ifstream Test ( "test.txt" );//load input file
    
    if (!Test)
    {//error message and termination on failure
        cout << "There was an error opening the file.\n";
        return 1;
    }
    else
    { //store words from input file in 1D vector
        while(getline(Test,line))
        {
            vector<string> tokens;//1D vector stores individual words
            stringstream ls(line);//used to parse words from each line
            string token;//string used to apply words to 1D vector
            while (ls>>token)
                tokens.push_back(token);//adds each word to 1D vector
            if(tokens.size())//adds 1D vector to 2D vector, if not empty
                addresses.emplace_back(move(tokens));
            else   //upon encountering empty line, add 2D vector to 
                {  //3D vector, then clear 2D vector for next address
                    book.emplace_back(move(addresses));
                    addresses.clear();
                }
        }
        /*
        for (auto& line : addresses)
        {   //inner loop outputs words in each line with spacing
            for (auto& token : line)
            {   if (token!="\n")//prevent duplicate empty line and incorrect spacing
                cout<<token<<" ";
            }
            cout<<endl;
        }*/
        //How can my triple output loop be more like above?
        for (unsigned int z=0;z<book.size()+1;z++)
        {
            for (unsigned int y=0;y<book[z].size();y++)
            {
                for (unsigned int x=0;x<book[z][y].size();x++)
                {
                    cout<<book[z][y][x]<<" ";
                }
                cout<<endl;
            }
            if (z!=book.size())//prevent duplicate blank line afterwards
            cout<<endl;
        }
     return 0;
    }
}

Steven Seagal
1234 Post Drive
Ventura, CA 93002

Adam Sandler
356 Golf Street
Calabasas, CA 91302

Johnny Depp
47 Castaway Cove
Rancho Palos Verdes, CA 90275

Snoop Dogg
1147 Long Beach Blvd
Unit 225
Long Beach, CA 90802

etc...
Last edited on
I got the output loop part. I just needed to iterate through each address in 'book', while keeping the other two as the inner two loops, and making use of the same output method. It works, but still fails to store or output the last address.
1
2
3
4
5
6
7
8
9
10
11
12
        for (auto& addresses : book)
        {   //outer loop iterates through addresses
            for (auto& line : addresses)
            {  //middle loop iterates through lines
                for (auto& token : line)
                {//inner loop iterates through words
                    cout<<token<<" ";
                }
                cout<<endl;//go to next line at end of each line
            }
            cout<<endl;//go to next line at end of each address
        }

I tried changing my condition for adding to the 3D vector in line 32 to
else if (tokens.size()<1 || Test.eof())
But it resulted in the same output. I also tried
else if (tokens.size()<1 || !getline(Test,line))
But it also resulted in the same behavior. I know from further experimentation that the words for the last address are being read into the 2d vector, yet they are not being added to the 3d one. What am I doing wrong?
Last edited on
Ok, I figured it out. In the end the answer was pretty simple; I needed to add the last address AFTER the loop. Here is the whole program, hopefully it proves helpful to anyone else attempting to tackle this topic in the future.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;

int main()
{
    string line;//string used to read in each line from file
    vector<vector<string> > addresses;//2D vector used to store addresses in grid format
    vector<vector<vector<string> > > book;//3D vector used to store addresses like pages

    ifstream Test ( "test.txt" );//load input file

    if (!Test)
    {
        //error message and termination on failure
        cout << "There was an error opening the file.\n";
        return 1;
    }
    else
    {
        //store words from input file in 1D vector
        while(getline(Test,line))
        {
            vector<string> tokens;//1D vector stores individual words
            stringstream ls(line);//used to parse words from each line
            string token;//string used to apply words to 1D vector
            while (ls>>token)
                tokens.push_back(token);//adds each word to 1D vector
            if(tokens.size())//adds 1D vector to 2D vector, if not empty
                addresses.emplace_back(move(tokens));
            else   
            {   //upon encountering empty line, add 2D vector to
                //3D vector, then clear 2D vector for next address
                book.emplace_back(move(addresses));
                addresses.clear();
            }
            tokens.clear();//clear 1D vector
        }
        book.emplace_back(move(addresses));//add last address to 3D vector
        addresses.clear();//clear 2D vector

        for (auto& addresses : book)
        {
            //outer loop iterates through addresses
            for (auto& line : addresses)
            {
                //middle loop iterates through lines
                for (auto& token : line)
                {
                    //inner loop iterates through words
                    cout<<token<<" ";
                }
                cout<<endl;//go to next line at end of each line
            }
            cout<<endl;//go to next line at end of each address
        }
        return 0;
    }
}
Last edited on
Topic archived. No new replies allowed.