printing a struct with a map in it - c++

Ok I am having trouble printing the values in my list of structs. The normal way of printing a list (with the iterator) is not working. This is my code of me reading in a file into my list.
struct Edge{
map<char, string> edge;
int weight= -1;
Edge(){};
Edge(char v, string e, int w){
edge.insert(pair<char,string>(v,e));
weight = w;
}
};
int main(){
list<Edge> edges;
//Read in file//
string line;
char start;
vector<string> tokens;
if(!file.is_open()){
cout<<"Could not open file\n";
}else{
while(getline(file,line)){
tokens.clear();
start = line.front();
istringstream iss(line);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter<vector<string>>(tokens));
for(int i = 1; i<tokens.size(); i+=2){
//cout << "Position: " <<i << " = " << tokens.at(i) <<endl;
//insert into edges list.
Edge e = Edge(start, tokens.at(i), stoi(tokens.at(i+1)));
edges.push_back(e);
}//end for
}//end while
}//end if-else
return 0;
}//end main
The vector tokens is read in properly. I checked it with the commented out cout.
The file is a graph file with the first element the start vertex and the rest of the line is formatted with the end vertex of the edge and the weight of that edge.
For example:
1 2 3 4 5
Would mean edge (1,2) has a weight of 3 and edge (1,4) has a weight of 5.
I don't know if I read in my list properly because I can't figure out how to print it out. How would I print out my list edges ?
Or is there a better way to set up my struct? Perhaps another struct for the edge and then a struct with the edge and weight?
Printing tried that won't work. The syntax doesn't even work. :(
The regular way to print a list. But doesn't like it since I have a struct list.
list<Edge>::iterator it;
for(it=edges.begin(); it!=edges.end(); it++){
cout << *it <<endl;
}//end for
And this was what I found when I searched. This is what I found.
C++ How to loop through a list of structs and access their properties
This is my attempt.
//inside main
list<Edge>::iterator it;
for(int i = 0; i<edges.size(); i++){
for_each(edges.begin(), edges.end(), printEdges);
}//end for
//outside main
void printEdges(Edge &data){
cout << "( " << data.edge << " )"
<< ": Weight = " << data.weight <<endl;
}//end printEdges
Thank you.

Update to comment:
friend std::ostream& operator<<(std::ostream& os, Edge const& edge)
{
os << "Edge { weight:" << edge.weight << " {\n";
for(edge_map::const_iterator it=edge.edge.begin(); it!=edge.edge.end(); ++it)
{
os << "{ '" << it->first << "', \"" << it->second << "\" } ";
}
return os << "\n}";
}
Should get you started.
it's int main, not void main
it's weight = w;, not weight = w.
You need to increment the loop variable. i+2 has no effect. Did you mean i+=2?
If you did, the loop condition should be fixed to avoid out-of-bounds addressing:
for(size_t i = 1; i+1<tokens.size(); i+=2){
note the use of size_t to avoid mixed signed/unsigned comparison
the Edge constructor should initialize weight
If you need //crutch comments, your code should be more legible :_)
Consider using a parser to read your input. You severely lack input validation, format flexibility and error handling.
Oh, don't use using namespace std
Here's an attempt at fixing some of the issues:
#include <string>
#include <map>
#include <list>
#include <vector>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <algorithm>
struct Edge{
typedef std::map<char, std::string> edge_map;
edge_map edge;
int weight;
Edge() : edge(), weight(0) {};
Edge(char v, std::string e, int w) : edge(), weight(w)
{
edge.insert(std::pair<char,std::string>(v,e));
weight = w;
}
friend std::ostream& operator<<(std::ostream& os, Edge const& edge)
{
os << "Edge { weight:" << edge.weight << " {\n";
for(edge_map::const_iterator it=edge.edge.begin(); it!=edge.edge.end(); ++it)
{
os << "{ '" << it->first << "', \"" << it->second << "\" } ";
}
return os << "\n}";
}
};
int main()
{
std::list<Edge> edges;
std::string line;
std::vector<std::string> tokens;
std::ifstream file("input.txt");
if(!file.is_open()){
std::cout<<"Could not open file\n";
}else{
while(std::getline(file,line)){
tokens.clear();
char start = line.front();
std::istringstream iss(line);
std::copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter<std::vector<std::string>>(tokens));
for(size_t i = 1; i+1<tokens.size(); i+=2){
Edge e = Edge(start, tokens.at(i), stoi(tokens.at(i+1)));
edges.push_back(e);
}
}
}
}

Related

c++ stack overflow due to recursive function how can I improve the data handling

I'm tackling a exercise which is designed to cause exactly this problem, of overloading the memory. Pretty much I'm loading various file sizes from 1,000 to 5 million lines of entries like this in a txt file (1 line = 1 entry):
SHFIv,aiSdG
PlgNB,bPHoP
ZHWJU,gfwgC
UAygL,Vqvhi
BlyzX,LLbCo
jbvrT,Utblj
...
pretty much every entry has 2 values separated by comma, in my code, I separate these values and try to find another matching value, there are always only 2 exactly matching values and each time 1 value is found the other one with which it is paired points to another pair, and so on until the final one gets found.
For example SHFIv,aiSdG would point to aiSdG,YDUVo.
I know my code is not very efficient, partly due to using recursion, but I could'nt figure out a better way to do the job, so any suggestions on how to possibly improve it to handle larger inputs would be greatly appriciated
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <map>
#include <unordered_map>
#include <stdio.h>
#include <vector>
#include <iterator>
#include <utility>
#include <functional>
#include <algorithm>
using namespace std;
template<typename T>
void search_bricks_backwards(string resume, vector<T>& vec, vector<string>& vec2) {
int index = 0;
for (const auto& pair : vec) {
//cout << "iteration " << index << endl;
if (pair.second == resume) {
vec2.insert(vec2.begin(), resume);
cout << "found " << resume << " and " << pair.second << endl;
search_bricks_backwards(pair.first, vec, vec2);
}
if (index + 1 == vec.size()) {
cout << "end of backward search, exitting..." << endl;
}
index++;
}
}
template<typename T>
void search_bricks(string start, vector<T>& vec, vector<string>& vec2) {
int index = 0;
for (const auto& pair : vec) {
//cout << "iteration " << index << endl;
if (pair.first == start) {
vec2.push_back(start);
cout << "found " << start << " and " << pair.first << endl;
search_bricks(pair.second, vec, vec2);
}
if (index + 1 == vec.size()) {
//search_bricks_backwards(start, vec, vec2);
// this also gets called on every recursion rather than just once
// as I originally intended when the forward iteration gets finished
}
index++;
}
}
template<typename T> // printing function
void printVectorElements(vector<T>& vec)
{
for (auto i = 0; i < vec.size(); ++i) {
cout << "(" << vec.at(i).first << ","
<< vec.at(i).second << ")" << endl ;
}
cout << endl;
}
vector<string> split(string s, string delimiter) { // filtering function
size_t pos_start = 0, pos_end, delim_len = delimiter.length();
string token;
vector<string> res;
while ((pos_end = s.find(delimiter, pos_start)) != string::npos) {
token = s.substr(pos_start, pos_end - pos_start);
pos_start = pos_end + delim_len;
res.push_back(token);
}
res.push_back(s.substr(pos_start));
return res;
}
int main()
{
vector<pair<string, string>> bricks;
vector<string> sorted_bricks;
ifstream inFile;
inFile.open("input-pairs-5K.txt"); // transferring data from .txt to a string
stringstream strStream;
strStream << inFile.rdbuf();
string str = strStream.str();
istringstream iss(str);
for (string line; getline(iss, line); )
// filtering data from string and dividing on ","
{
string delimiter = ",";
string s = line;
vector<string> v = split(s, delimiter);
string s1 = v.at(0);
string s2 = v.at(1);
bricks.push_back(make_pair(s1, s2));
}
search_bricks(bricks[0].second, bricks, sorted_bricks);
//printVectorElements(bricks);
//for (auto i = sorted_bricks.begin(); i != sorted_bricks.end(); ++i)
//cout << *i << " "; // this is just to check if vectors have data
}
Here is link to the 1k test data that works for me (only for the search bricks without backwards searching since it triggers on every recursion) again thanks for any suggestions on how to improve or get rid of the recursion. I don't code in c++ often and don't really know how else to tackle this.
Although implementing non-recursive version of your algorithm is canonical solution, if you really need to solve the problem without code modification, you can increase the stack size by modifying compiler option. ~100Mb will be usually sufficient.
In MSVC : /STACK:commit 104857600
In gcc : --stack, 104857600

To read FILE and store data in map<string, vector<string>> c++98

I have file bird.lst, I need to read file contents and store data in map<string, vector>, here the idea is bird name is store in string and those having some attribute values that needs to be stored in vector. please help
eventually map<string, vector> looks like below,
ex:
parrot.sh ----> eat yes
fly yes
file contents below of bird.lst
parrot.sh
eat yes
fly yes
pigeon.sh
eat yes
fly yes
duck.sh
eat yes
fly no
flammingo.sh
eat yes
fly yes
eagle.sh
eat yes
flay yes
You need a nested loop.
The outside one reads the name of the bird (the key of the map)
The inside one reads the attributes of the bird (the values of the vector)
Here is what I came up with:
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <vector>
typedef std::vector<std::string> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;
int main()
{
std::ifstream file("bird.lst");
bird_map birds;
std::string key;
while(std::getline(file,key))
{
attribute_vector attributes;
std::string value;
while(std::getline(file,value))
{
// in case it has windows encoding with end-of-line = \r\n
if (!value.empty() &&
value[value.size()-1] == '\r')
{
value.erase(value.size() - 1);
}
// if we found the empty string
if(value.empty())
{
break;
}
// save the value into the vector
attributes.push_back(value);
}
// save the bird into the map
birds[key] = attributes;
}
// now print the data we collected
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
bird++)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
attribute++)
{
std::cout << " " << *attribute << "\n";
}
std::cout << "\n";
}
return 0;
}
Try it at https://onlinegdb.com/1TBobUxE2 (it says C++17 as the compiler type but in the config under "Extra Compiler Flags" I am passing -std=c++98)
If you want to split the attribute from the yes/no value then:
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
typedef std::pair<std::string,std::string> attribute_pair;
typedef std::vector<attribute_pair> attribute_vector;
typedef std::map<std::string,attribute_vector> bird_map;
int main()
{
std::ifstream file("bird.lst");
bird_map birds;
std::string key;
while(std::getline(file,key))
{
attribute_vector attributes;
std::string value;
while(std::getline(file,value))
{
// in case it has windows encoding with end-of-line = \r\n
if (!value.empty() &&
value[value.size()-1] == '\r')
{
value.erase(value.size() - 1);
}
// if we found the empty string
if(value.empty())
{
break;
}
// now split the value into an attribute and a flag
attribute_pair attribute;
std::istringstream ss(value);
ss >> attribute.first >> attribute.second;
// save the value into the vector
attributes.push_back(attribute);
}
// save the bird into the map
birds[key] = attributes;
}
// now print the data we collected
for(bird_map::iterator bird = birds.begin();
bird != birds.end();
bird++)
{
std::cout << bird->first << "\n";
for(attribute_vector::iterator attribute = bird->second.begin();
attribute != bird->second.end();
attribute++)
{
std::cout << " " << attribute->first
<< " = " << attribute->second
<< "\n";
}
std::cout << "\n";
}
return 0;
}
Try it at https://onlinegdb.com/Htlh4eHu9 (it says C++17 as the compiler type but in the config under "Extra Compiler Flags" I am passing -std=c++98)
(Issue) The problem is not very specific.
So I'll try to make a very general solution.
Define a function:
bool is_file(string s) {
return (s.substr((int)s.size() - 3, 3) == ".sh");
}
Telling you if a string is a bird file.
Because of (Issue), I'll asume each attribute of the bird is of the form: (attribute, yes/no), wich we'll transform to (attribute, 1/0).
Now, to read the file, you have several options. I'll name two of them.
Pass the file from console, i.e, if you .exe is called birds.exe, just do birds.exe<bird.lst. And just read with std::cin.
Use freopen("bird.lst", "r", stdin); and just read with std::cin.
Then, the main function should look like this:
int main () {
freopen("bird.lst", "r", stdin); // if you didnt read from console.
map<string, vector<pair<string, bool>>> birds;
string current_bird;
while (cin >> s) {
string s;
cin >> s;
if (is_file(s)) {
current_bird = s;
continue;
}
bool verdict;
cin >> verdict;
bird[current_bird].push_back(make_pair(s, verdict));
}
}
And to print the data:
for (auto it = birds.begin(); it != birds.end(); it++) {
cout << "Bird File: " << it.first << "\n";
cout << "Attributes:\n";
for (auto x : it.second) cout << x.first << " " << (x.second ? "YES" : "NO") << "\n";
}

How to print certain elements from vector?

I am trying to print out whatever is necessary from my program. What it does is it takes a long list from a text file and sort it based on first choice and GPA and put it into a vector. I manage to sort by First choice and GPA however how can I remove whatever output that isn't necessary?
This is an example of my Txt File (The sequence of each line is 1st choice, 2nd choice, 3rd choice, GPA, Name):
CC,DR,TP,3.8,AlexKong
SN,SM,TP,4,MarcusTan
DR,TP,SC,3.6,AstaGoodwin
SC,TP,DR,2.8,MalcumYeo
SN,SM,TP,3.7,DavidLim
SN,SM,TP,3.2,SebastianHo
SC,TP,DR,4,PranjitSingh
DR,TP,SC,3.7,JacobMa
and so on...
This is my output now (it is a long vector):
TP,DR,SC,4,SitiZakariah
TP,DR,SC,3.9,MuttuSami
TP,DR,SC,3.5,SabrinaEster
TP,DR,SC,3,KarimIlham
TP,DR,SC,3,AndryHritik
SN,SM,TP,4,MarcusTan
SN,SM,TP,3.8,MarcusOng
SN,SM,TP,3.7,DavidLim
SN,SM,TP,3.4,MollyLau
SN,SM,TP,3.2,SebastianHo
SN,SM,TP,3.2,NurAfiqah
SN,SM,TP,2.4,TanXiWei
SC,TP,DR,4,SallyYeo
SC,TP,DR,4,PranjitSingh
SC,TP,DR,3.6,RanjitSing
SC,TP,DR,2.8,MalcumYeo
SC,TP,DR,2.8,AbdulHalim
SC,TP,DR,2.7,AlifAziz
DR,TP,SC,3.9,SitiAliyah
DR,TP,SC,3.9,LindaChan
DR,TP,SC,3.8,SohLeeHoon
DR,TP,SC,3.7,PrithikaSari
DR,TP,SC,3.7,NurAzizah
DR,TP,SC,3.7,JacobMa
DR,TP,SC,3.6,AstaGoodwin
CC,DR,TP,3.9,MuruArun
CC,DR,TP,3.8,AlexKong
CC,DR,TP,3.7,DamianKoh
CC,DR,TP,3.3,MattWiliiams
CC,DR,TP,3.3,IrfanMuhaimin
And this is the output that I need (Basically students with CC as their 1st choice without displaying the 3 options):
3.9,MuruArun
3.8,AlexKong
3.7,DamianKoh
3.3,MattWiliiams
3.3,IrfanMuhaimin
This is my program.
#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
struct greater
{
template<class T>
bool operator()(T const &a, T const &b) const { return a > b; }
};
void main()
{
vector<string> v;
int p = 0;
ifstream File;
File.open("DSA.txt");
if (!File.is_open()) return;
string First;
cout << "Round 1:\n";
while (File >> First)
{
v.push_back(First);
p++;
}
for (int i = 0; i < v.size(); i++)
{
sort(v.begin(), v.end(), greater());
cout << v[i] << endl;
}
}
your last for loop:
for (int i = 0; i < v.size(); i++)
{
sort(v.begin(), v.end(), greater());
cout << v[i].substr(9) << endl;
}
EDIT:
If you want to only display ones with CC as 1st choice you can add if statement to your loop:
for (int i = 0; i < v.size(); i++)
{
if (v[i].substr(0,2) != "CC") continue;
cout << v[i].substr(9) << endl;
}
Also, I noticed another problem in your code. You should not sort the vector at every iteration. You should do it only once before the loop:
sort(v.begin(), v.end(), greater());
for (int i = 0; i < v.size(); i++)
{
if (v[i].substr(0,2) != "CC") continue;
cout << v[i].substr(9) << endl;
}
as I propose in the comment,
since the data is well defined as a structure, you can interpret semantically each row and filter according to that: here is what am talking about
int main()
{
std::vector<std::string> v;
std::string r = "CC,DR,TP,3.9,MuruArun";
std::string delimiter = ",";
std::string token = r.substr(0, r.find(delimiter));
if(token == ??)// compare to what ever you want
{
v.emplace_back(r);
}
cout << "token: " << token << endl;
cout << v.size() << endl;
return 0;
}

How to fix my topological.cpp outputting error?

i have been provided middleearth.h/cpp and was asked to make a makefile, doxyfile (which i did correctly) and a topological.cpp that works but has a small mistake in the output and i need help with that please.ill provide all three files and the text we use to test and the error.
cs2110 cs2150
cs2102 cs2150
cs1110 cs2110
cs3330 cs4414
cs2150 cs4414
cs2110 cs3330
cs1110 cs2102
0 0
middleearth.h
#ifndef MIDDLEEARTH_H
#define MIDDLEEARTH_H
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <random>
using namespace std;
// see the comments in the lab11 write-up, or in middleearth.cpp
class MiddleEarth {
public:
MiddleEarth(int xsize, int ysize, int num_cities, int seed);
void print();
void printTable();
float getDistance(const string& city1, const string& city2);
vector<string> getItinerary(unsigned int length);
private:
int num_city_names, xsize, ysize;
unordered_map<string, float> xpos, ypos;
vector<string> cities;
unordered_map<string, unordered_map<string, float>> distances;
mt19937 gen; // Mersenne-Twister random number engine
};
#endif
middleearth.cpp
#include "middleearth.h"
#include <algorithm>
#include <array>
#include <cstdlib>
#include <cmath>
// New shuffle method that uses the Mersenne Twister engine
void shuffle (vector<string>::iterator first, vector<string>::iterator last, mt19937& g) {
for (auto i=(last-first)-1; i>0; --i) {
unsigned int n = (g() / (double) g.max())*distance(first,last);
swap (first[i], first[n]);
}
}
// The list of all the place names that we'll be using
const array<string, 40> all_city_names{
// human towns, cities and strongholds
"Bree", // a human and hobbit town between the Shire and Rivendell
"Isengard", // the tower fortress where Saruman resided; Gandalf was imprisoned there.
"Minas Tirith", // capital of Gondor, the "white city"; home to Boromir, Denethor, and later, Aragorn
"Osgiliath", // city on the river Anduin; is at the other end of Pelennor Fields from M. Tirith
"Edoras", // the capital city of Rohan, where King Theoden resides
"Helm's Deep", // fortress of Rohan, it is where the people of Edoras fled to from the orc invasion
"Dunharrow", // a refuge of Rohan, it is where Elrond presents the sword to Aragorn in the movie
// dwarf cities
"Moria", // the enormous dwarven underground complex that the Fellowship traveled through
// elvish cities
"Lothlorien", // the elvish tree-city, home of Lady Galadriel and Lord Celeborn
"Rivendell", // the elvish city that is home to Lord Elrond
"The Grey Havens", // the port city on the western coast from which the elves travel westward
// hobbit villages
"Bucklebury", // a Shire village, it has a ferry across the Brandywine River that the Hobbits use
"Bywater", // a Shire village, it is the site of the Battle of Bywater (removed from the movie)
"Hobbiton", // a Shire village, it is home to Bilbo and, later, Frodo
"Michel Delving", // a Shire village, it is the chief town of the Shire
// Mordor places
"Orodruin", // Mount Doom in Mordor, it is where the Ring was made, and later, unmade
"Barad-Dur", // Sauron's fortress that was part castle, part mountain
"Minas Morgul", // formerly the Gondorian city of Minas Ithil; renamed when Sauron took it over
"Cirith Ungol", // the mountianous pass that Sam & Frodo went through; home of Shelob
"Gorgoroth", // the plains in Mordor that Frodo & Sam had to cross to reach Mount Doom
// places that are not cities
"Emyn Muil", // the rocky region that Sam & Frodo climb through after leaving the Fellowship
"Fangorn Forest", // the forest where Treebeard (and the other Ents) live
"Dagorlad", // great plain/swamp between Emyn Muil & Mordor where a great battle was fought long ago
"Weathertop", // the tower between Bree and Rivendell where Aragorn and the Hobbits take refuge
"Gladden Fields", // this is where the Ring is lost in the River Anduin, after Isildur is ambushed and killed by Orcs
"Entwash River", // a river through Rohan, which flows through Fangorn Forest
"River Isen", // river through the Gap of Rohan; Theoden's son was slain in a battle here.
"The Black Gate", // huge gate to Mordor that Aragorn and company attack as the ring is destroyed
"The Old Forest", // a forest to the west of the Shire (adventures there were removed from the movie)
"Trollshaws", // area to the west of Rivendell that was home to the trolls that Bilbo met
"Pelennor Fields", // great plain between M. Tirith and Osgiliath; site of the Battle of M. Tirith
"Hollin", // the empty plains that the Fellowship crosses between Rivendell and Moria
"Mirkwood", // Legolas' forest home; Bilbo travels there in 'The Hobbit'.
"Misty Mountains", // the north-south moutain range that runs through Middle-earth
"Prancing Pony", // an inn in Bree where the hobbits tried to meet Gandalf, but meet Aragorn instead
// places from the Hobbit book and movies
"Laketown", // also called Esgaorth, it is the town of men on the Long Lake near Erebor
"Dale", // the town of men outside Erebor, destroyed by Smaug long before the Hobbit story
"Erebor", // the Elvish name for the Lonely Mountain, where the dwarves had their fortress
"Beorn's House", // Beorn is the shape-shifter who shelters the dwarf party
"Dol Guldur", // fortress in Mirkwood where Sauron, as the Necromancer, hid during most of the Hobbit
};
// Iluvatar, the creator of Middle-Earth
MiddleEarth::MiddleEarth(int xsize, int ysize, int num_cities, int seed) {
this->xsize = xsize;
this->ysize = ysize;
// set up the random number generator
gen.seed(seed == -1 ? random_device{}() : seed);
// count the number of cities in the array
this->num_city_names = all_city_names.size();
if (num_cities > num_city_names) {
cout << "There are only " << num_city_names << " city names, so "
<< num_cities << " cities cannot be created." << endl;
cout << "Exiting." << endl;
exit(0);
}
if (num_cities < 5) {
num_cities = 5;
}
// copy all the cities into a mutable vector
this->cities = vector<string>(all_city_names.begin(), all_city_names.end());
shuffle(cities.begin(), cities.end(), gen); // shuffle all the cities
cities.erase(cities.begin() + num_cities, cities.end()); // then remove the ones we won't be using
// compute random city positions
for (auto city : cities) {
xpos.emplace(city, (gen() / (double) gen.max()) * xsize);
ypos.emplace(city, (gen() / (double) gen.max()) * ysize);
}
// compute the 2-d distance array
// we assume that num_cities < xsize * ysize
for (auto city1 : cities) {
for (auto city2 : cities) {
distances[city1].emplace(city2, sqrt((xpos[city2] - xpos[city1]) * (xpos[city2] - xpos[city1]) +
(ypos[city2] - ypos[city1]) * (ypos[city2] - ypos[city1])));
}
}
}
// The Mouth of Sauron!
// Prints out info on the created 'world'
void MiddleEarth::print() {
cout << "there are " << num_city_names
<< " locations to choose from; we are using " << cities.size() << endl;
cout << "they are: " << endl;
for (auto city : cities) {
cout << "\t" << city << " # (" << xpos[city] << ", " << ypos[city]
<< ")" << endl;
}
}
// Prints a tab-separated table of the distances,
// which can be loaded into Excel or similar
void MiddleEarth::printTable() {
cout << "Table: " << endl << endl << "Location\txpos\typos\t";
for (auto city : cities) {
cout << city << "\t";
}
cout << endl;
for (auto city1 : cities) {
cout << city1 << "\t" << xpos[city1] << "\t" << ypos[city1] << "\t";
for (auto city2 : cities) {
cout << distances[city1][city2] << "\t";
}
cout << endl;
}
}
// This method returns the distance between the two passed cities.
// If we assume that the hash table (i.e. the map) is O(1),
// then this method call is also O(1)
float MiddleEarth::getDistance(const string& city1, const string& city2) {
return distances[city1][city2];
}
// Returns the list of cities to travel to.
// The first city is the original start point as well as the end point.
// The number of cities passed in does not include this start/end point
// (so there will be length+1 entries in the returned vector).
vector<string> MiddleEarth::getItinerary(unsigned int length) {
// check parameter
if (length >= cities.size()) {
cout << "You have requested an itinerary of " << length
<< " cities; you cannot ask for an itinerary of more than length "
<< cities.size() - 1 << endl;
exit(0);
}
length++; // to account for the start point
// we need to make a deep copy of the cities vector
vector<string> itinerary(cities.begin(), cities.end());
// shuffle, erase unneeded ones, and return the itinerary
shuffle(itinerary.begin(), itinerary.end(), gen);
itinerary.erase(itinerary.begin() + length, itinerary.end());
return itinerary;
}
topological.cpp
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <list>
#include <stack>
#include <string>
#include <map>
using namespace std;
/**
#date 11/16/2020
*/
/** #brief
*/
class Graph
{
public:
Graph(int vert);
/** #brief adds an edge to the list
* #param string v
* #param string w
* #param map<string, int> m
*
*/
void addEdge(string v, string w, map<string, int> m);
void printSort();
private:
int vertices;
list<int> *myList;
void sort(int v, stack<int> &myStack, bool visited[]);
map<int, string> myMap;
};
Graph::Graph(int vert) {
vertices = vert;
myList = new list<int>[vertices];
}
void Graph::addEdge(string v, string w, map<string, int> m) {
int loc1 = m[v];
int loc2 = m[w];
myMap[loc1] = v;
myMap[loc2] = w;
myList[loc1].push_back(loc2);
}
void Graph::sort(int v, stack<int> &myStack, bool visited[]) {
visited[v] = true;
list<int>::iterator i;
for(i = myList[v].begin(); i != myList[v].end(); ++i) {
if(!visited[*i]) {
sort(*i, myStack, visited);
}
}
myStack.push(v);
}
void Graph::printSort() {
stack<int> myStack;
bool *visited = new bool[vertices];
for(int i = 0; i < vertices; i++) {
visited[i] = false;
}
for(int i = 0; i < vertices; i++) {
if(visited[i] == false)
sort(i, myStack, visited);
}
while(myStack.empty() == false) {
int x = myStack.top();
string output = myMap[x];
cout << output << " ";
myStack.pop();
}
}
int main (int argc, char **argv) {
if ( argc != 2 ) {
cout << "Must supply the input file name as the one and only parameter" << endl;
return 1;
}
ifstream file(argv[1], ifstream::binary);
ifstream file1(argv[1], ifstream::binary);
if ( !file.is_open() ) {
cout << "Unable to open file '" << argv[1] << "'." << endl;
return 1;
}
string s1, s2;
int count = 0;
list <string> edges;
while(!file.eof()) {
file >> s1;
file >> s2;
if(s1 == "0" && s2 == "0") {
break;
}
edges.push_back(s1);
edges.push_back(s2);
}
file.close();
edges.sort();
edges.unique();
int size = edges.size();
map<string, int> myMap;
Graph myGraph(size);
list<string>::iterator i;
for(i = edges.begin(); i != edges.end(); i++) {
string s = *i;
myMap[s] = count;
count++;
}
while(!file1.eof()) {
file1 >> s1;
file1 >> s2;
if(s1 == "0" && s2 == "0") {
break;
}
myGraph.addEdge(s1, s2, myMap);
}
myGraph.printSort();
cout<<endl;
file1.close();
return 0;
}
You are confusing yourself. You have your solution in edges. There isn't a reason to read the data a second time. For example, you can simply output sorted/unique elements of edges, e.g. the modifications to your code are:
int main (int argc, char **argv) {
if ( argc != 2 ) {
cout << "Must supply the input file name as the one and only parameter" << endl;
return 1;
}
ifstream file(argv[1], ifstream::binary);
// ifstream file1(argv[1], ifstream::binary);
if ( !file.is_open() ) {
cout << "Unable to open file '" << argv[1] << "'." << endl;
return 1;
}
string s1, s2;
// int count = 0;
list <string> edges;
while(file >> s1 && file >> s2) {
if(s1 == "0" && s2 == "0") {
break;
}
edges.push_back(s1);
edges.push_back(s2);
}
file.close();
edges.sort();
edges.unique();
// int size = edges.size();
// map<string, int> myMap;
bool first = true;
for (const auto& n : edges) {
if (!first)
cout.put(' ');
cout << n;
first = false;
}
cout.put('\n');
// Graph myGraph(size);
// list<string>::iterator i;
// for(i = edges.begin(); i != edges.end(); i++) {
// string s = *i;
// myMap[s] = count;
// count++;
// }
//
// while(file1 >> s1 && file1 >> s2) {
// if(s1 == "0" && s2 == "0") {
// break;
// }
// myGraph.addEdge(s1, s2, myMap);
// }
//
// myGraph.printSort();
// cout<<endl;
// file1.close();
return 0;
}
(note: how while (!file.eof())) was replaced with while(file >> s1 && file >> s2))
Example Use/Output
With your sample data in dat/topological.txt, you would receive:
$ ./bin/topological dat/topological.txt
cs1110 cs2102 cs2110 cs2150 cs3330 cs4414
If you are having problems editing your code, then you can do things much easier with a std::set, e.g.
#include <iostream>
#include <fstream>
#include <string>
#include <set>
int main (int argc, char **argv) {
if ( argc != 2 ) {
std::cerr << "Must supply the input file name as the one and only parameter\n";
return 1;
}
std::set<std::string> strset {};
std::string s {};
std::ifstream f (argv[1]);
if (!f.good()) {
std::cerr << "file open failed.\n";
return 1;
}
while (f >> s && s != "0")
strset.insert(s);
bool first = true;
for (const auto& unique : strset) {
if (!first)
std::cout.put(' ');
std::cout << unique;
first = false;
}
std::cout.put('\n');
}
(same answer)
This is when i used the main you gave me:
enter image description here
This is when i added (Yes. after edges.unique(); you can simply do. bool first = true; for (const auto& n : edges) { if (!first) cout.put(' '); cout << n; first = false; } cout.put('\n');):
enter image description here
the first line on output is correct, i dont want the line under it

How to print words using reverse_iterator in STL

I have to read two text files and then compare words from second file with the first one. Then , I have to display KnownWords which are same words from both files and the remaining words which are not same are UnknownWords. Next Step is, I have to display most frequent known words in DisplayMostFreqKnownWords() and unknown words in DisplayMostFreqUnknownWords() functions. I have successfully completed DisplayMostFreqKnownWords() and so far Output is alright. I copied the same code from DisplayMostFreqKnownWords() to DisplayMostFreqUnknownWords() but in this is function it is not showing anything in the output. I dont know what is wrong. Can someone figure this one out.
Output is:
Displaying most frequent known words
Word Count
the 19
a 14
of 11
artificial 11
that 10
to 7
signal 7
and 7
in 6
they 5
Displaying most frequent unknown words
Word Count
Header file:
typedef map<string, vector<int> > WordMap;
typedef WordMap::iterator WordMapIter;
class WordStats
{
public:
WordStats();
void ReadDictionary();
void DisplayDictionary();
void ReadTxtFile();
void DisplayKnownWordStats();
void DisplayUnknownWordStats();
void DisplayMostFreqKnownWords();
void DisplayMostFreqUnknownWords();
private:
WordMap KnownWords;
WordMap UnknownWords;
WordMapIter Paragraph;
set<string> Dictionary;
char Filename[256];
}
My program:
// Displays 10 most frequent words in KnownWords
void WordStats::DisplayMostFreqKnownWords(){
int count;
multimap<int,string > displayFreqWords;// new map with int as key
(multimap because key could occur more than once)
multimap<int,string >::reverse_iterator rit = displayFreqWords.rbegin();
for (Paragraph = KnownWords.begin(); Paragraph != KnownWords.end();
++Paragraph){ // iterate map again
string word = (*Paragraph).first;
int cnt = (*Paragraph).second.size();
displayFreqWords.insert(pair<int,string>(cnt,word));
}
// multimap<int,string>::iterator rit; // iterator for new map
cout <<" Word Count\n";
for(; count<=10 && rit!=displayFreqWords.rend(); rit++, ++count){
string word = (*rit).second;
int cnt = (*rit).first;
cout << setw(15) << word << setw(10) << cnt << endl;
}
}
// Displays 10 most frequent words in UnknownWords
void WordStats::DisplayMostFreqUnknownWords(){
int count;
multimap<int,string > displayFreqUnknownWords;
multimap<int,string >::reverse_iterator rrit =
displayFreqUnknownWords.rbegin();
for (Paragraph = UnknownWords.begin(); Paragraph !=
UnknownWords.end(); ++Paragraph){
string word = (*Paragraph).first;
int cnt = (*Paragraph).second.size();
displayFreqUnknownWords.insert(pair<int,string>(cnt,word));
}
// multimap<int,string>::iterator rit; // iterator for new map
cout <<" Word Count\n";
for(; count<=10 && rrit!=displayFreqUnknownWords.rend(); rrit++, ++count){
string wrd = (*rrit).second;
int ccnt = (*rrit).first;
cout << setw(15) << wrd << setw(10) << ccnt << endl;
}
}
Here's a way to express what I think is your use case. I have used c++17 tuple expansion.
I have used unordered_map to deduce which words are known or unknown, and two multimaps to determine known and unknown word frequency.
Hope it's helpful.
#include <sstream>
#include <tuple>
#include <string>
#include <unordered_map>
#include <algorithm>
#include <iterator>
#include <map>
#include <iostream>
#include <iomanip>
#include <fstream>
// Set this to 1 to run a static test
#define TESTING 0
#if TESTING
using input_type = std::istringstream;
std::tuple<input_type, input_type> open_inputs() {
return {
std::istringstream("the big black cat sat on the grey mat"),
std::istringstream("the gold small cat lay on the purple mat")
};
}
#else
using input_type = std::ifstream;
std::tuple<input_type, input_type> open_inputs() {
return {
std::ifstream("left_file.txt"),
std::ifstream("right_file.txt"),
};
}
#endif
struct Counts {
int left_count = 0, right_count = 0;
int total() const {
return left_count + right_count;
}
bool is_known() const {
return left_count && right_count;
}
};
template<class F>
void for_each_word_in_file(std::istream &is, F f) {
std::for_each(std::istream_iterator<std::string>(is),
std::istream_iterator<std::string>(),
f);
}
int main() {
// open files
auto[left, right] = open_inputs();
auto known_words = std::unordered_map<std::string, Counts>();
// count words in each file
for_each_word_in_file(left, [&known_words](auto &&word) {
++known_words[word].left_count;
});
for_each_word_in_file(right, [&known_words](auto &&word) {
++known_words[word].right_count;
});
// map counts to words, in descending order, allowing multiple entries of the same count
std::multimap<int, std::string, std::greater<>> known_ordered, unknown_ordered;
// iterate all words seen, putting into appropriate map
for (auto&&[word, counts] : known_words) {
(counts.is_known() ? known_ordered : unknown_ordered)
.emplace(counts.total(), word);
}
// emit results
std::cout << "Known words by frequency\n";
for (auto&&[freq, word] : known_ordered) {
std::cout << std::setw(15) << word << " " << freq << '\n';
}
std::cout << "\nUmknown words by frequency\n";
for (auto&&[freq, word] : unknown_ordered) {
std::cout << std::setw(15) << word << " " << freq << '\n';
}
}