Class template - declaring data type <T> which contains value for particular element

I have a file called grad.dat which contains data on numerous post-secondary graduates. The records are formatted as follows: <year province degree gender numEmployed numGrads>

I am trying to use the NGS data provided in order to generate a total of five (5) reports with different statistics. For example, the first report will calculate the employment percentage for each region, by degree, for all years and all genders. Each region will be a row, and each type of degree will be a column. Each cell will show the percentage who were employed in that region, with that degree, for all years and all genders combined, compared to the number of graduates in that region

The program will read in the graduate data from the grad.dat file and present the user with a menu of possible reports. When the user selects a report, the program will compute the statistics required for the report using the graduate data, and print the information to the screen.

The program will implement an inheritance hierarchy of control objects that are responsible for report generation. The base class of this hierarchy will be the abstract ReportGenerator class, and there will be one derived, concrete class for each type of report that the user can run. The primary control object will store a collection of report generator objects, one for each type of report. In order to show a menu to the user, the primary control object will query each report generator in its collection, retrieve the name of each report, and present those names to the user as the menu options, which will be numbered.

MY ISSUE:

Right now I’m really confused with various parts of the Property class/what it’s supposed to be used for. I don’t understand what 1) means by declaring a value of data type which contains the value for that particular element. This relates to 2) where it says to make a collection with pointers to records that have the specific property value. My attempt at this was Record* recordCollection; but I don’t think this is what it’s asking for. I guess I don’t quite understand what the task means by declaring a specific property value/how I would do that. I think I did 4) correctly where I make a function [] that returns the record pointer at the given index.

I can probably guess 3) once I figure out the first two. I would assume it’s just taking an element in the parameter and adding it to the recordCollection that’s declared in step 2) by using a for loop or something. I don’t expect anyone to help with the ReportGenerator class, but I included the information below for the class to give context for the Property class. I would really appreciate any help or guidance! I’ve never used class templates before so this makes it more confusing for me.

ReportGenerator class information:

1. A static data member that stores the primary collection of all the NGS data that is read in from the data file. I will be an STL vector of record pointers

2. several static data members that store partial collections of the data, but organized in a way that facilitates the generation of reports; these collections must be used when generating the reports

(a) define, populate, and traverse for report generation the following: a partial collection organized by year, one organized by region, and one organized by degree

(b) each partial collection will be defined as a collection of Property object pointers; each Property object stores a collection that is a subset of the records in the primary data collection; the records in a Property object are there because they meet a specific criteria

(c) each element inside these partial collections will hold the records for one specific value of a Property. For ex: one of the properties is year so the ReportGenerator base class will contain a partial collection organized by year; let’s say we call that partial collection allYears. The NGS data contains statistics for only four different years (2000, 2005, 2010, and 2015), so the allYears partial collection will contain exactly four elements, one for each year; the first element of the allYears collection will contain all the records for the year 2000; the second element will contain those for the year 2005, and so on for the years 2010 and 2015

Property class information:

The partial collection data members described above will each be defined as a collection of Property object pointers, where Property is declared as a class template that is specialized for the data type of that property.

For example, because the year property is an integer, the allYears collection will contain pointers to Property objects that are specialized for the integer data type (so where is instantiated to int). The partial collections organized by properties that are strings will store pointers to Property objects that are specialized for the string data type.

You will define the Property class template to contain:

1) a value of data type , which contains the value for that particular element
(a) for example, the first Property element in the partial collection organized by year will have the value 2000 to represent the year 2000
(b) the first Property element in in the partial collection organized by region will have the value “AB” to represent the province of Alberta

2) a collection that contains pointers to all the data records that have the specific property value
(a) for example, the first Property element in the year partial collection will contain the data records for the year 2000
(b) the first Property element in the region partial collection will contain the data records for the province of Alberta The Property class template must overload the following operators:

3) the addition assignment operator += that adds a new element to the back of the record collection.

4) subscript operator [] that returns the record pointer at the given index


Record.cc

1
2
3
4
5
6
7
8
9
10
11
12
	#include <iostream>
	#include <string>
	using namespace std;
	#include "Record.h"

	Record::Record(int i1, string s1, string s2, string s3, int i2, int i3) : 
	year(i1), province(s1), degree(s2), gender(s3), numEmployed(i2), numEmployed(i3){
	  
	}

	Record::~Record(){
	}


Record.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

	#ifndef RECORD_H
	#define RECORD_H

	#include <iostream>
	#include <string>

	class Record{
	  public:
		Record(int = 0, string = "Unknown", string = "Unknown", string = "Unknown", int = 0, int = 0);
		~Record();
		
		private:
			int year;
			string province;
			string degree;
		string gender;
			int numEmployed;		
			int numGrads;
	};

	#endif 


ReportGenerator.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
	#include <iostream>
	using namespace std;
	#include <string>

	#include "ReportGenerator.h"

	ReportGenerator::ReportGenerator(){ 
	  reportName = NULL;
	}

	void ReportGenerator::loadRecords(){}

	void ReportGenerator::execute(string& outStr){}


ReportGenerator.h

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
	#ifndef REPORTGENERATOR_H
	#define REPORTGENERATOR_H
	#define MAX_YEARS 4
	#define MAX_REGIONS 11
	#define MAX_DEGREES 4

	#include <iostream>
	using namespace std;

	#include "Record.h"
	#include "Property.h"

	class ReportGenerator{
	  public:
		static void loadRecords();
		virtual void execute(string& outStr) = 0;
		
	  
	  protected:
		static vector<Record*> dataCollection;
		Property* allYears[MAX_YEARS]; //4 years
		Property* allRegions[MAX_REGIONS]; //11 different regions
		Property* allDegrees[MAX_DEGREES]; //4 diff degrees
		string reportName;
		
	};


Property.h

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
	#ifndef PROPERTY_H
	#define PROPERTY_H

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

	template <class T>

	class Property{
		public:
			 Property& operator+=(const int);
			 Property& operator[](int);
		private:
			 Record* recordCollection;
	}


	template <class T>
	Property& Property::operator+=(){

	}

	template <class T>
	Record& Property::operator[](int index){
	  if (index < 0) {
		cout << "ERROR:  invalid index" << endl;
		exit(1);
	  }
	  return recordCollection[index];
	}

	#endif 


grad.dat

1
2
3
4
5
6
7
8
9
10
	2000 AB Bachelor's All 6900 7500
	2005 AB Bachelor's All 9300 10000
	2015 PE Bachelor's All 440 500
	2000 PE College All 728 800
	2005 AB Bachelor's Females 5642 6200
	2010 AB Bachelor's Females 5369 5900
	2015 BC Doctorate Females 188 200
	2010 BC Doctorate Males 285 300
	2005 CAN Bachelor's Males 33396 36300
	2000 CAN College Males 28569 32100
May be something along these lines:

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
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <string_view>

// record.h
struct record {

    int year;
    std::string province;
    std::string degree;
    std::string gender;
    int numEmployed;
    int numGrads;
};

    // property
    // --------

    // 1. a value of data type , which contains the value for that particular element is the key
    // KEY value ;

    // 2. a collection that contains pointers to all the data records that have the specific property value
    // std::vector< const record* > data ;

    // a map is better for look ups. key is our KEY, mapped value is the vector of pointers
    // https://en.cppreference.com/w/cpp/container/map

// property_map.h
template < typename KEY >
    using property_map = std::map< KEY, std::vector< const record* > > ;

// report_generator.h
struct report_generator { // consider making this a namespace
                          // instead of a class with all static members

    // primary collection of all the NGS data that is read in from the data file.
    // It will be an STL vector of record pointers
    // note: consider using a std::list<record> instead of this vector of pointers
    static std::vector<record*> primary_data ;

    // partial collections of the data, but organized in a way that facilitates the generation of reports
    static property_map<int> years ; // key is the year (int)
    static property_map<std::string_view> provinces ; // key is the province (string, we use a string_view to avoid copying the string)
    // etc.

    // populate the partial collections
    static void populate() {

        for( const record* r : primary_data ) { // for each record r in the primary collection

            years[ r->year ].push_back(r) ; // add an entry to the years collection
            provinces[ r->province ].push_back(r) ; // add an entry to the provinces collection
            // etc.
        }
    }
};


Using a partial collection for report generation is quite straight forward. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// populate the data in report_generator
// ...

// print out the total numEmployed for each year

// for each year and its associated vector
for( const auto& [ year, vec ] : report_generator::years ) {

    std::cout << "year: " << year ;

    // sum up the values of numEmployed for all the records in the associated vector
    unsigned long long total_numEmployed = 0 ;
    for( const record* r : vec ) total_numEmployed += r->numEmployed ;

    std::cout << "   total numEmployed: " << total_numEmployed << '\n' ;
}


Topic archived. No new replies allowed.