Using String Arrays and Maps

Hello Everyone,

This is my first post and I am really at the very beginner level of C++ (few days in) so lets see how this goes.

I wrote a script in PHP but I am now trying to convert it into C++ for the sake of learning. What I am having trouble with is converting PHP multi-dimensional associative arrays into C++ and being able to manage these arrays.

So, I will start with the original PHP code that I have:

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
$selectedGenres = array(
	'cartoons',
	'bestSitcoms',
);

$genres = array(
	'cartoons' => array(
		'Archer',
	),
	'bestSitcoms' => array(
		'Other Show',
	),
	'otherSitcoms' => array(
		'Bad Show',
	),
);

//method from another class that I am trying to replicate in c++
public function setShows($genres, $selectedGenres)
{
	$this->shows = array();
	
	foreach ($selectedGenres as $selectedGenre)
	{
		foreach ($genres[$selectedGenre] as $show)
		{
			$this->shows[] = $show;
		}
	}
}


So, what I am doing is setting up a list of "shows" and putting them into their appropriate genres (the $genres array). Then, I am taking a filter ($selectedGenres array) and am generating a new array of strings with just those shows. The method setShows() in this example would make the $this->shows array contain the strings "Archer" and "Other Show" but would leave out "Bad Show" because this genre is not in the filter of selectedGenres.

Now, that is the basic thing I am trying to do in C++. So far, I have gotten here to this point:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <string>
#include <map>
#include <iostream>
#include <vector>

#include "Classes/shows.cpp"

using namespace std;

int main()
{
	shows showsClass;
	vector<string> result;
	
	string selectedGenres [] = {
		"cartoons",
		"bestSitcoms",
		"otherSitcoms",
	};
	int selectedGenresSize = sizeof(selectedGenres) / sizeof(selectedGenres[0]);
	
	map<string, map<int, string> > genres;
	
	map<int, string> cartoons;
	cartoons[0] = "Archer";
	
	map<int, string> bestSitcoms;
	bestSitcoms[0] = "Other Show";

	map<int, string> otherSitcoms;
	bestSitcoms[0] = "Bad Show";
	
	genres["cartoons"] = cartoons;
	genres["bestSitcoms"] = bestSitcoms;
	
	showsClass.setShows(genres, selectedGenres, selectedGenresSize);
}

/** FILE Classes/shows.cpp below **/
#ifndef __SHOWS_H_INCLUDED__
#define __SHOWS_H_INCLUDED__

using namespace std;

class shows
{
	protected:
		string shows [];
	public:
		vector<string> setShows(map<string, map<int, string> > genres, string selectedGenres [], int selectedGenresSize)
		{
			vector<string> result;
			map<string, map<int, string> >::iterator iter;
			
			for (int i = 0; i < selectedGenresSize; i++)
			{
				cout << "+ " << selectedGenresSize << " + " << i << " + " << selectedGenres[i] << endl;
				
				for (iter = genres.begin(); iter != genres.end(); ++iter)
				{
					if (selectedGenres[i] == iter->first)
					{
						cout << ". " << iter->first << endl;
					}
				}
			}
			
			return result;
	
	
			/*foreach ($selectedGenres as $selectedGenre)
			{
				foreach ($genres[$selectedGenre] as $show)
				{
					$this->shows[] = $show;
				}
			}*/
		}
};

#endif 


Now, what the point this has gotten me to is the ability to use the filter to somewhat get the map of genres to use the filter, but I don't know how to get a list of items within this map. So, I can get it to say "this iteration of the map is good" but I can't get "add the values stored within this iteration to the result vector".

So, my questions are:
1) How can I get the string values stored in the map iteration out and add them to the result vector?
2) Am I doing things correctly or is this just some really bad code?

Any advice or comments, no matter how harsh, is more than welcome.
I do not know PHP but I have written something that maybe will be useful for you. It is important that your compiler will support some features of the new C++ Standard.

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
#include <iostream>
#include <iomanip>
#include <map>
#include <set>
#include <string>
#include <algorithm>

int main()
{
   std::map<std::string, std::string> m;

   m["cartoons"]        = "Archer";
   m["bestSitcoms"]  = "Other Show";
   m["otherSitcoms"] = "Bad Show";


   /* Everywhere in the code if your compiler supports 'for' based on range 
       then you can use it instead of algorithm for_each. For example
       for ( const auto &x : m )
       {
          std::cout << std::setw( 12 ) << std::left << x.first 
                        << ' ' << x.second << std::endl;
       }
   */

   std::for_each( m.begin(), m.end(),
                         []( std::pair<std::string, std::string> x ) 
                         { std::cout << std::setw( 12 ) << std::left << x.first 
                                          << ' ' << x.second << std::endl; } );
   std::cout << std::endl;

   std::set<std::string> s;

   s.insert( "cartoons" );
   s.insert( "bestSitcoms" );

   std::for_each( s.begin(), s.end(),
                         []( std::string x ) 
                         { std::cout << std::setw( 12 ) << std::left << x << std::endl; } );

   std::cout << std::endl;

   std::map<std::string, std::string> m1;

   std::copy_if( m.begin(), m.end(), std::inserter( m1, m1.begin() ),
                       [&s]( std::pair<std::string, std::string> x )
                      { return ( s.count( x.first ) != 0 ); } );

   std::for_each( m1.begin(), m1.end(),
                        []( std::pair<std::string, std::string> x ) 
                        { std::cout << std::setw( 12 ) << std::left << x.first 
                         << ' ' << x.second << std::endl; } );
   std::cout << std::endl;

   return ( 0 );
}


The ouput is

bestSitcoms Other Show
cartoons Archer
otherSitcoms Bad Show

bestSitcoms
cartoons

bestSitcoms Other Show
cartoons Archer
Last edited on
Your function should be
1
2
3
4
5
6
vector<string> setShows(map<string, vector<string> > genres, vector<string> selectedGenres) {
   vector<string> shows;
   for(int i = 0; i < selectedGenres.size(); i++)
      shows.insert(shows.end(), genres[selectedGenres[i]].begin(), genres[selectedGenres[i]].end())
   return shows;
}
Although I don't know php either.
hamsterman,

why do not use std::multimap<std::string, std::string> instead of so strange construction as map<string, vector<string> >?

Vlad, thank you for your post, it is quite helpful but still is missing a few things.

I didn't quite make myself very clear before but first, in the genres map, I will have multiple strings for each key (genre). So, my map will actually look like this:

1
2
3
4
5
6
7
8
9
10
map<string, map<int, string> > m;

//not exact values, but the idea is the same.
m["cartoons"][0] = "Cartoon1";
m["cartoons"][1] = "Cartoon2";
m["cartoons"][1] = "Cartoon3";
m["cartoons"][1] = "Cartoon4";
m["bestSitcoms"][0] = "BestSitcom1";
m["otherSitcoms"][0] = "OtherSitcom1";
m["otherSitcoms"][0] = "OtherSitcom2";


Second, the for_each calls are being a bit silly. I am not sure if it is the compilers fault or something else (using g++ gcc version 4.4.5 (Debian 4.4.5-8)) but I am getting a compilation error on each for_each call. The errors are:


test.cpp:44: error: expected primary-expression before ‘[’ token
test.cpp:44: error: expected primary-expression before ‘]’ token
test.cpp:44: error: expected primary-expression before ‘x’


I read the docs on for_each and the last argument is the function to call to run through the data. Cool, but I would like the functions to be inline (like in your post) but this is giving errors. How can I keep the function definition inline and within the same scope as the call itself without getting the compilation errors?

EDIT: Sorry, I didn't see the next 2 responses. Will give these things a look and see what happens
Last edited on
Ok, I changed things up a bit, no longer using for_each and using the for loop instead as hampster man showed.

I got the code to work properly though but I doubt it is done the best way possible. What I ended up with is:

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
//create-playlist.cpp
#include <string>
#include <map>
#include <iostream>
#include <vector>

#include "Classes/shows.cpp"

using namespace std;

int main()
{
	shows showsClass;
	vector<string> result;
	
	//setup the genres that we care to see
	vector<string> selectedGenres;
	selectedGenres.push_back("cartoons");
	selectedGenres.push_back("bestSitcoms");
	
	//setup all of the genres and add the shows available in each genre
	map<string, vector<string> > genres;
	
	//create the cartoons genre
	vector<string> cartoons;
	cartoons.push_back("Cartoon 1");
	cartoons.push_back("Cartoon 2");
	
	//create the bestSitcoms genre
	vector<string> bestSitcoms;
	bestSitcoms.push_back("Best Sitcom 1");
	bestSitcoms.push_back("Best Sitcom 2");
	
	//create the otherSitcoms genre
	vector<string> otherSitcoms;
	otherSitcoms.push_back("Bad Show 1");
	
	//add the genres with their shows into the main genres map
	genres["cartoons"] = cartoons;
	genres["bestSitcoms"] = bestSitcoms;
	genres["otherSitcoms"] = otherSitcoms;
	
	//set the "good to go" shows in the showsClass based off our filters
	showsClass.setShows(genres, selectedGenres);
	
	showsClass.showShows();
	
	return 0;
}


and

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
//Classes/shows.cpp
#ifndef __SHOWS_H_INCLUDED__
#define __SHOWS_H_INCLUDED__

using namespace std;

class shows
{
	protected:
		vector<string> shows;
	public:
		void setShows(map<string, vector<string> > genres, vector<string> selectedGenres)
		{
			for (int i = 0; i < selectedGenres.size(); i++)
			{
				shows.insert(shows.end(), genres[selectedGenres[i]].begin(), genres[selectedGenres[i]].end());
			}
		}
		
		void showShows()
		{
			//for error debugging
			vector<string>::iterator showsIterator;
			
			cout << "List of good-to-go shows" << endl;
			
			int i = 0;
			for (showsIterator = shows.begin(); showsIterator < shows.end(); showsIterator++)
			{
				i++;
				cout << i << ") " << *showsIterator << endl;
			}
			//end debugging
		}
};

#endif 


Vlad, can you tell me what you mean by using multimap instead of map<string, vector<string> >? I tried it but got a compilation error and wasn't able to figure it out myself.

Also, any code critiques would be awesome.
If you use several strings for one key as in your example then you should use std::multimap container. In this case you can not use subscripting operator. If you want to use your notation as

m["cartoons"][0] = "Cartoon1";
m["cartoons"][1] = "Cartoon2";
m["cartoons"][1] = "Cartoon3";
m["cartoons"][1] = "Cartoon4";


maybe std::map<std::string, std::vector<std:;string>> should be used indeed but if you want that they were ordered you should sort every vector.

As for the error it seems that your compiler does not support new standard features as lambda expressions. In this case you should to write functional object instead of lambda expression. Or you can use a simple loop. For example

1
2
3
4
5
6
for ( std::map<std::string, std::string>::iterator it = m.begin();
       it != m.end(); ++it )
{
   std::cout << std::setw( 12 ) << std::left << it->first 
                 << ' ' << it->second << std::endl;
}
Topic archived. No new replies allowed.