I'm currently having a real difficult time trying to sort my array (I really need to work on this, I always have trouble sorting data).
So basically I have created a C++ struct that contains a "date" and "snowDepth" element. I have used bubblesort logic to sort the snowDepth element, which is awesome and that's exactly what I need. However, since the elements are separate, the dates don't change. How can I sort a parallel array??
Data should look like this:
Snow Report December 12 - 18
Date Base
13 42.3
12 42.5
14 42.8
15 43.1
18 43.1
16 43.4
17 43.8
and mine looks like this:
SNOW REPORT December 12 - 18
=======================================
DATE DEPTH
12 42.3
13 42.5
14 42.8
15 43.1
16 43.1
17 43.4
18 43.8
Notice how the dates aren't sorted along with the snowDepth element. How can I accomplish this?
Okay, thanks for all the comments guys!! Here is my code:
// Structure
struct Array
{
int date;
double snowDepth;
void SnowData(int d, double sD)
{
date = d;
snowDepth = sD;
}
};
// Main Function
int main()
{
/* *--------------------*
| VARIABLES |
*--------------------* */
// Constant Variables
const int NUM_ELEMENTS = 7;
const int NUM_MONTHS = 12;
// String, Numerical, and Boolean Variables
string month;
int startDate;
int endDate;
int a;
int b;
int flag = 0;
double result;
bool monthMatch = false;
// Array Variables
Array snowData[NUM_ELEMENTS];
string name[NUM_MONTHS] = {"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December" };
/* *--------------------*
| USER INSTRUCTIONS |
*--------------------* */
cout << endl;
cout << "This program will keep track of the local snow conditions for 7 days." << endl;
cout << "This program will ask the user for the month, date range (must be 7 days)," << endl;
cout << "and the inches of snow reported on that date. The program will then sort" << endl;
cout << "the data from least amount of snow reported to the most." << endl;
cout << endl;
/* *--------------------*
| USER PROMPT |
*--------------------* */
// Prompt the user for the month
cout << "Enter the full name of the month: ";
cin >> month;
// Prompt the user for the starting date
cout << "Enter the starting date of the 7-day period: ";
cin >> startDate;
snowData[0].date = startDate;
// Once the user enters the start date, run a loop to initialize the rest of the dates in snowData array
for (int x = 1; x < NUM_ELEMENTS; x++)
snowData[x].date = ++startDate;
// Prompt the user for the ending date
cout << "Enter the ending date of the 7-day period: ";
cin >> endDate;
cout << endl;
// If the user does not enter the correct ending date (which is 7 days from the start date), loop will prompt the user again
if (endDate != snowData[NUM_ELEMENTS - 1].date)
{
do
{
cout << "The end date entered is not 7 days from the start date. Try again." << endl;
cout << "Enter the ending date of the 7-day period: ";
cin >> endDate;
cout << endl;
} while (endDate != snowData[NUM_ELEMENTS - 1].date);
int x = 0;
// Once the user enters the correct ending date, prompt the user for the snow depth numbers
for (int y = 0; y < NUM_ELEMENTS; y++)
{
cout << "Enter the depth of snow measured on " << month << " " << snowData[x].date << ": ";
cin >> snowData[y].snowDepth;
x++;
}
}
// Once the user enters the correct ending date, prompt the user for the snow depth numbers
else
{
int x = 0;
for (int y = 0; y < NUM_ELEMENTS; y++)
{
cout << "Enter the depth of snow measured on " << month << " " << snowData[x].date << ": ";
cin >> snowData[y].snowDepth;
x++;
}
}
/* *--------------------*
| SORTING & FORMAT |
*--------------------* */
// Sorting logic in ascending order
for (a = 1; (a <= NUM_ELEMENTS) && flag; a++)
{
flag = 0;
for (b = 0; b < (NUM_ELEMENTS - 1); b++)
{
if (snowData[b + 1].snowDepth < snowData[b].snowDepth)
{
result = snowData[b].snowDepth;
snowData[b].snowDepth = snowData[b + 1].snowDepth;
snowData[b + 1].snowDepth = result;
flag = 1;
}
}
}
// Formatted Output
cout << endl;
cout << " SNOW REPORT ";
cout << month << " " << snowData[0].date << " - " << snowData[6].date << endl;
cout << "=======================================" << endl;
cout << setw(11) << "DATE" << setw(18) << "DEPTH" << endl;
for (int x = 0; x < (NUM_ELEMENTS); x++)
cout << setw(10) << snowData[x].date << setw(18) << snowData[x].snowDepth << endl;
cout << endl;
}
You could create a struct for a single date/snowDepth pair, and keep an array of such pairs. After you could add more fields there, and sort by whatever you like. The data will always be consistent.
By the way: why bubble sort? Quicksort is much faster.
Since both elements is in the same struct, the two elements should move together. Your array should be an array of structs. How do you have it configured now?
Update Added a std::map based example for good measure (see below)
I wouldn't work on sorting as much as on datastructures :)
Here is a sample that I wrote in order to brush-off the dust of my C++ skills :)
Sorry if I threw in more than the kitchen sink.
Also note, that for this 'tivial' datatype you could probably use std::pair as the 'Report' struct and std::map as the container type, requiring substantially less manual coding, but I wanted to show you how things are done if you coded the struct manually:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
static struct Report
{
int date;
double base;
Report(int d, double b) : date(d), base(b) {}
bool operator<(const Report& rhs) const
{
return (date<rhs.date) || ((date==rhs.date) && (base<rhs.base));
}
friend std::ostream& operator<<(std::ostream& os, const Report& r)
{
os << "Report(" << r.date << ", " << r.base << ")";
}
} theData[] = {
Report( 13, 42.3),
Report( 12, 42.5),
Report( 14, 42.8),
Report( 15, 43.1),
Report( 18, 43.1),
Report( 16, 43.4),
Report( 17, 43.8),
};
int main(int argc, const char* args[])
{
Report *const begin = theData;
Report *const end = theData+(sizeof(theData)/sizeof(*theData));
// simpler container, take a copy for comparison
std::vector<Report> simplerData(begin, end);
// just sort it
std::sort(begin, end);
// VERIFY by printing to console
std::cout << "Verify sorted array: " << std::endl;
std::copy(begin, end, std::ostream_iterator<Report>(std::cout, "\n"));
std::cout << "=====================" << std::endl;
std::cout << "Verify unsorted copy:" << std::endl;
std::copy(simplerData.begin(), simplerData.end(), std::ostream_iterator<Report>(std::cout, "\n"));
std::cout << "=====================" << std::endl;
// Sort that as well - for fun, reversed
std::sort(simplerData.begin(), simplerData.end(), std::not2(std::less<Report>()));
std::cout << "Verify reversed copy:" << std::endl;
std::copy(simplerData.begin(), simplerData.end(), std::ostream_iterator<Report>(std::cout, "\n"));
std::cout << "=====================" << std::endl;
}
Compile and run using:
g++ -o test test.cpp
./test
Output:
Verify sorted array:
Report(12, 42.5)
Report(13, 42.3)
Report(14, 42.8)
Report(15, 43.1)
Report(16, 43.4)
Report(17, 43.8)
Report(18, 43.1)
=====================
Verify unsorted copy:
Report(13, 42.3)
Report(12, 42.5)
Report(14, 42.8)
Report(15, 43.1)
Report(18, 43.1)
Report(16, 43.4)
Report(17, 43.8)
=====================
Verify reversed copy:
Report(18, 43.1)
Report(17, 43.8)
Report(16, 43.4)
Report(15, 43.1)
Report(14, 42.8)
Report(13, 42.3)
Report(12, 42.5)
=====================
For completeness, here's what it would look like when using std::map as I suggested above. Note that ordering by keys is implicit:
namespace std // hacky, google Koenig lookup...
{
template <class K, class V> static std::ostream& operator<<(std::ostream& os, const std::pair<K, V> p)
{ os << "[" << p.first << ", " << p.second << "]"; }
}
void static UsingStdMap()
{
typedef std::map<int, double> Reports;
typedef Reports::value_type Report;
static const Report initializer[] = {Report(13,42.3),Report(12,42.5),Report(14,42.8),Report(15,43.1),Report(18,43.1),Report(16,43.4),Report(17,43.8)};
Reports theMap(initializer, initializer+sizeof(initializer)/sizeof(*initializer));
std::copy(theMap.begin(), theMap.end(), std::ostream_iterator<Report>(std::cout, "\n"));
}
Did I mention I'm a bit lazy? Hence the hack to display the items using stream operator.
To use custom ordering, see here for example
If your homework assignment is specifically about learning to write a sorting algorithm, replace this code:
// Sorting logic in ascending order
...
result = snowData[b].snowDepth;
snowData[b].snowDepth = snowData[b + 1].snowDepth;
snowData[b + 1].snowDepth = result;
...
with this:
// Sorting logic in ascending order
...
Array tmp = snowData[b];
snowData[b] = snowData[b + 1];
snowData[b + 1] = tmp;
...
If your assignment is not about learning to write a sort, use std::sort:
bool Array::operator<(const Array& rhs) const { return this->snowDepth < rhs.snowDepth; }
...
// Sorting logic in ascending order
std::sort(snowData, snowData+NUM_ELEMENTS);
P.s. Your data type Array is misnamed. It doesn't hold an array, it holds a single observation.
Since you've asked this under a C++ tag, here's one way in C++:
typedef std::pair<Date, double> SnowReportItem;
std::vector<SnowReportItem> SnowReport;
struct sort_criteria {
bool operator()(const SnowReportItem &a, const SnowReportItem &b)
{ return a.second < b.second; }
};
SnowReport.push_back(make_pair(Date("12 Feb 2011"), 42.5));
// similar ones for the others ...
std::sort(SnowReport.begin(), SnowReport.end(), sort_criteria);
Also, see this stackoverflow question.
EDIT: std::pair.second is not a function.
You were so close! It's correct that you compare the snowDepth values of the array elements, but you shouldn't just re-arrange those values. Instead, you should re-arrange the entire Array value.
Instead of this:
result = snowData[b].snowDepth;
snowData[b].snowDepth = snowData[b + 1].snowDepth;
snowData[b + 1].snowDepth = result;
Do this:
Array temp = snowData[b];
snowData[b] = snowData[b + 1];
snoData[b + 1] = temp;
That will move both the snowDepth value and the date value (because they're members of the same struct).
You're already storing the data in a struct, so the simplest thing would actually be to just define operator< and swap for your Array type.
struct Array
{
int date;
double snowDepth;
void SnowData(int d, double sD)
{
date = d;
snowDepth = sD;
}
friend bool operator<(const SnowData &a, const SnowData &b)
{
return a.date < b.date;
}
friend void swap(SnowData &a, SnowData &b)
{
using std::swap;
swap(a.date, b.date);
swap(a.snowDepth, b.snowDepth);
}
};
// ...later... sort the array
std::sort(snowData, snowData + NUM_ELEMENTS);
Related
My first C++ class coming from a basic Java class. This class is a more advanced C++ programming class about Data Structures. I don't know the basics of C++, only a little basics of Java.
Assignment is to :
-get 3 user inputs of states and their population (done).
-Get the most populated (biggest of three) and post it. (1/2)
I am able to get the highest number... but I'm not sure on the syntax on how to post it with the corresponding string (state).
I know this is some kind of array using struct, but I dont know how to post st.title
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct states_t {
string statename;
int population;
} state[3];
int main()
{
string mystr;
int n;
for (n = 0; n<3; n++)
{
cout << "Enter state name: ";
getline(cin, state[n].statename);
cout << "Enter population: ";
getline(cin, mystr);
stringstream(mystr) >> state[n].population;
}
cout << "\nYou have entered these movies:\n";
for (n = 0; n < 3; n++)
cout << state[n].statename << "\n" << state[n].population << "\n";
return 0;
}
==== UPDATED CODE WITH LARGEST POPULATION ====
#include "stdafx.h"
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct states_t {
string statename;
int population;
} state[3];
int main()
{
string mystr;
int n;
for (n = 0; n<3; n++)
{
cout << "Enter state name: ";
getline(cin, state[n].statename);
cout << "Enter population: ";
getline(cin, mystr);
stringstream(mystr) >> state[n].population;
}
cout << "\nYou have entered these states:\n";
for (n = 0; n < 3; n++)
cout << state[n].statename << " " << state[n].population << "\n" << "\n";
if ((state[0].population >= state[1].population) && (state[0].population >= state[2].population))
cout << "The most populous state you entered is: " << state[0].statename << " with a population of " << state[0].population << "\n";
else if ((state[1].population >= state[0].population) && (state[1].population >= state[2].population))
cout << "The most populous state you entered is: " << state[1].statename << " with a population of " << state[1].population << "\n";
else
cout << "The most populous state you entered is: " << state[2].statename << " with a population of " << state[2].population << "\n";
return 0;
}
The first step is to store the name of each state with its population. It will help if you change title to name to make it more clear what the variable is for. If you do this correctly, you will quickly see that you no longer need mystr. (Note that you should always use meaningful variable names. A generic name like mystr often means that you do not know the purpose of the variable. Keep thinking about what the variable is for in order to make a more useful name.)
Now once you have the state data input into the array correclty, you should keep track of the data for the least and most populous state, rather than just its population. Instead of
int mn, mx;
declare
state_t mn, mx;
Then in your if statement do
mn = st[n];
and similarly for mx.
You will have to change your if condition to access the value in the struct. Then you can print the values directly from mn and mx.
Your code is designed to find the highest (and lowest) population of all states. You could also have tried to find out what the index-number is of the state with the highest population and use that number to index the state array to get what you need from there.
Here's how I would do it:
I would first make two int arrays, one corresponding to the index values of the array of struct states_t, and then one corresponding to the population values, like such:
int index[3];
int pop[3];
for (int i = 0; i < 3; i++)
{
index[i] = i;
pop[i] = st[i].population;
}
Next, perform a bubble sort algorithm on the population, and move the indices of the objects around according to the actions of the sort algorithm like such:
int n = 3;
for (int i = 0 ; i < ( n - 1 ); i++)
{
for (int j = 0 ; j < n - i - 1; j++)
{
//if the population of the next element
//is higher than the current element, swap it
//perform the same operation for state indices
if (array[j] > array[j+1])
{
int swap = pop[j];
int swap2 = index[j];
pop[j] = pop[j+1];
index[j] = index[j+1];
pop[j+1] = swap;
index[j+1] = swap2;
}
}
}
All that's left to do now is to call the first object in the list with the index array like such:
st[index[0]].title; //state with highest population
Coolest part about this method is that you can make this work for any number of States by changing the value of int n.
While there is nothing that prevents you from using a standard array-of-struct as you would in C, embracing the C++ std::vector can take a bulk of the tedium out of it. While using the array-of-struct, you get the benefit of protecting your array bounds manually indexing and manually handling storage, C++ has long sought to help alleviate or ease the manual aspects of handling collections of "things" (for lack of better words)
The std::vector container is tailor made to allow you to add to a collection of things using the simple push_back modifier. Basically you define your struct (say s_state_t) holding your name and pop, much as you have, and then declare and create an instance of a vector of type <s_state_t> (say state). Then to add a state (say s) to the vector you simply state.push_back(s) and let std::vector handle the storage and indexing. (vector also provides many other helpful member functions and modifiers to help get information about your collection)
Then the C++ way to approach managing a collection of states is to create either an additional struct or class to manipulate your collections of states, to add to, check and keep track of the max/min populations, etc. In a very simple form, you could create a class, that provides member functions that do just that, add a new state, check the max/min and then provide a way to output the contents of your collection. For example, you could do something like:
#include <vector>
#include <string>
#include <iomanip>
#include <limits>
typedef struct { /* struct holding state name and population */
std::string name;
int pop;
} s_state_t;
class country { /* class country - a collection of states */
std::vector<s_state_t> state; /* declare vector for states */
s_state_t mx = { "", 0 }, /* declare structs for max min */
mn = { "", std::numeric_limits<int>::max() };
void chkmxmn (s_state_t s) { /* function to set max/min */
if (s.pop < mn.pop)
mn = s;
if (s.pop > mx.pop)
mx = s;
}
public:
void addstate (std::string name, int pop) { /* add a state */
s_state_t s = { name, pop }; /* struct for new state */
chkmxmn (s); /* update max and min */
state.push_back (s); /* push_back to vector */
}
void prnstates() { /* output saved states, max/min */
for (auto& i : state) /* loop over vector */
std::cout << std::setw(16) << std::left << i.name <<
std::setw(10) << std::right << i.pop << "\n";
std::cout << "\nminimum and maximum populations:\n" <<
std::setw(16) << std::left << mn.name <<
std::setw(10) << std::right << mn.pop << "\n" <<
std::setw(16) << std::left << mx.name <<
std::setw(10) << std::right << mx.pop << "\n";
}
};
int main (void) {
country us; /* instance of country */
us.addstate ("Texas", 25000000); /* add names/pops */
us.addstate ("Louisiana", 12000000);
us.addstate ("California", 50000000);
us.prnstates(); /* output results */
return 0;
}
(note: you should add additional validations to check the name is not NULL or empty and pop is a reasonable number -- that is left to you)
Example Use/Output
$ ./bin/vector_states
Texas 25000000
Louisiana 12000000
California 50000000
minimum and maximum populations:
Louisiana 12000000
California 50000000
note: you can also create a typedef to your new vector type to cut down on the typing associated with specifying instances and parameters of the type with something similar to:
typedef std::vector<s_state_t> state_t; /* typedef to cut down typing */
which then allows you to declare new instances or parameters as simply, e.g.:
state_t state; /* declare vector for states */
Look things over. Neither method is more "right or wrong", but if you are going to learn C++ instead of C, you might as well go ahead and use the nice parts of it.
So I am trying to make a rental program that reads in a file with a list of cars ( year, make, model, price, and if available).
I also have to make options such as showing all cars and having the user look at a specific car( for example they type 1, and it will show the top car on the text file). This is where I am confused, I tried making several cars such as car1,car2, etc. But I am having trouble registering the first read car into car1.
This is an example file.
CarData.txt
2014 Toyota Tacoma 115.12 1
2012 Honda CRV 85.10 0
2015 Ford Fusion 90.89 0
2013 GMC Yukon 110.43 0
2009 Dodge Neon 45.25 1
2011 Toyota Rav4 65.02 1
2012 Mazda CX5 86.75 1
2016 Subaru Outback 71.27 0
2015 Ford F150 112.83 1
2010 Toyota Corolla 50.36 1
Source code:
#include <iostream>
#include <fstream>
using namespace std;
struct car
{
int year;
char make[10];
char model[10];
float price;
int available;
}car1[1], car2[1], car3[1], car4[1], car5[1], car6[1], car7[1], car8[1], car9[1], car10[1];
void copy_string(char d[][50], char s[][50]);
int my_strcmp(char *a, char *b);
// Main Function
int main ()
{
// declare variables
int choice;
car carLib[10];
char array[30][50];
char filename[10];
ifstream carInData;
//prompt user for input file
cout << " Enter file name: ";
cin >> filename;
cout << " 1 - Show Cars\n";
cout << " 2 - Rental Cost\n";
cout << " 3 - Most Expensive Car\n";
carInData.open(filename);
cin >> choice;
if(carInData.is_open());
{
int count = 0;
// read list of names into array
for( count; count < 1; count++){
carInData >> car1[count].year >> car1[count].make >> car1[count].model >> car1[count].price >> car1[count].available;
carInData >> car2[count].year >> car2[count].make >> car2[count].model >> car2[count].price >> car2[count].available;
switch (choice){
case 1:
cout << car1[count].year << " " << car1[count].make << " " << car1[count].model << " " << car1[count].price << " " << car1[count].available << " " << "\n";
cout << car2[count].year << " " << car2[count].make << " " << car2[count].model << " " << car2[count].price << " " << car2[count].available << " " << "\n";
break;
}
}
}
return 0;
}
// copy function
void copy_string(char d[], char s[]) {
int c = 0;
while (s[c] != '\0') {
d[c] = s[c];
c++;
}
d[c] = '\0';
}
// string copy
int my_strcmp(char *a, char *b)
{
while (*a && *b && *a == *b) { ++a; ++b; }
return *a - *b;
}
Here is example source, how to work with array of struct type, like car carLib[300];:
#include <iostream>
struct car
{
int year;
char make[10];
char model[10]; // that's only 9 letters for zero-terminated strings
float price;
int available;
};
int main()
{
constexpr size_t maxCarLib = 300;
car carLib[maxCarLib]; // array of memory reserved for several cars
size_t carLibCount = 0; // current allocation of array (zero cars entered)
carLib[carLibCount++] = car {1993, {"Nohda"}, {"XYZ"}, 123.45, 0};
carLib[carLibCount++] = car {1994, {"Frod"}, {"Peon"}, 543.21, 1};
carLib[carLibCount++] = car {1977, {"Nohda"}, {"ABC"}, 3.32, 0};
std::cout << "Cars in library: " << carLibCount << std::endl;
for (size_t carIndex = 0; carIndex < carLibCount; ++carIndex) {
const car & carI = carLib[carIndex];
printf("Car at index %lu: %d %s %s %f %d\n",
carIndex, carI.year, carI.make, carI.model, carI.price, carI.available);
}
// Find most expensive car
float mostExpensivePrice = 0; // most expensive price found
size_t mostExpensiveIndex = carLibCount; // index points beyond array ("no car")
for (size_t carIndex = 0; carIndex < carLibCount; ++carIndex) {
if (carLib[carIndex].price <= mostExpensivePrice) continue;
// the car at carIndex is more expensive than old maximum, remember it
mostExpensivePrice = carLib[carIndex].price;
mostExpensiveIndex = carIndex;
}
if (mostExpensiveIndex == carLibCount) {
std::cout << "Most expensive car not found." << std::endl;
} else {
const car & carI = carLib[mostExpensiveIndex];
printf("Most expensive car at index %lu: %d %s %s %f %d\n",
mostExpensiveIndex, carI.year, carI.make, carI.model, carI.price, carI.available);
}
}
Live demo
Mind you, this is just array indexing example, this "old C" code style is very error prone to index-out-of-bounds code, overwriting memory, or accessing wrong memory. That's why in modern C++ you can use std::vector<car> carLib; instead, which is much better fit for such task.
Vector example:
#include <iostream>
#include <vector>
struct car
{
int year;
char make[10];
char model[10]; // that's only 9 letters for zero-terminated strings
float price;
int available;
};
int main()
{
std::vector<car> carLib;
carLib.push_back(car {1993, {"Nohda"}, {"XYZ"}, 123.45, 0});
carLib.push_back(car {1994, {"Frod"}, {"Peon"}, 543.21, 1});
carLib.push_back(car {1977, {"Nohda"}, {"ABC"}, 3.32, 0});
std::cout << "Cars in library: " << carLib.size() << std::endl;
for (const auto & kar : carLib) {
printf("%d %s %s %f %d\n",
kar.year, kar.make, kar.model, kar.price, kar.available);
}
if (0 < carLib.size()) {
// indexing trough size_t index still possible with vector
std::cout << "Last car price is: " <<
carLib[carLib.size()-1].price << std::endl;
// Last valid index being size()-1, because first index is 0
}
}
But that C-like is good for you to study details about computer memory, also it does introduce you to the "float is not a good variable type for amounts", as the live demo shows. So open it in debugger, and find some memory window, and study how the data are actually laid out in memory, how 10 letter long name of car makes your struct problematic, etc...
The std::vector stores the struct in the same way as the bare array, only having few more helper data around, and reserving the memory dynamically as you need, so understanding the array example (and it's shortcomings) first is just as important.
I'm having a little trouble with my code. It's pretty much supposed to open two files, and compare the first twenty line of the file "StudentAnswers.txt" [inputted as a char into a char array] against a char value in (each line of another file) "CorrectAnswers.txt" in another array at the same position (index). It's like a linear search, but the same position in the arrays. Then a report should be displayed, detailing which question the student missed, the given answer, the correct answer, and if the student passed (got >= 70%) or not, like the following:
Report for Student X:
2 (A/D), 3 (C/D), 5(D/A)
This student passed the exam!
Then it should clear the SAArray, and feed the next twenty lines from StudentAnswers.txt, and start the process all over again. I guess the program has to determine the number of students from (lines of 'StudentAnswers.txt' file / 20).
I'm having trouble displaying the report, and having the array clear itself after the program. I'm guessing this can be done with a while loop and an accumulator for the number of students (to be determined by above equation).
Also, Visual Studio seems to go to "Missed __ questions for a total of ___ %", and then keep looping -858993460.
Any help would be appreciated.
#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <algorithm>
using namespace std;
void GradeReturn(char[], char[], int, int, int);
string PassFail(float);
int main()
{
ifstream SA("StudentAnswers.txt");
ifstream CA("CorrectAnswers.txt");char CAArray[20];
char SAArray[20];
// char SA2Array[20];
bool isCorrect;
int correct;
int incorrect;
int counter;
correct = 0;incorrect = 0;
counter = 0;
cout << endl;
if (!SA.fail())
{
cout << "'StudentAnswers.txt' file opened successfully." << endl;
cout << "'CorrectAnswers.txt' file opened successfully." << endl << endl;
int a = 0;
int b = 0;
while (a < 20)
{
CA >> CAArray[a];
a++;
} // while loop to feed char into the array
while (b < 20)
{
SA >> SAArray[b];
b++;
}
} // while loop to feed char into array
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
return 0;
}
void GradeReturn(char CAArray[], char SAArray[], int correct, int incorrect, int counter)
{
float percent;
float hundred;
int student;
int catcher[20];
int writeCatcher; int starter;
int catcher_size;
student = 0;
writeCatcher = 0;
catcher_size = ((sizeof catcher) / 4);
while (counter < 20)
{
if ((CAArray[counter]) == (SAArray[counter]))
{
correct++;
cout << "Good job!" << endl;
} // correct handling
else
{
incorrect++;
cout << "You got question " << counter << " wrong." << endl;
counter >> catcher[writeCatcher];
writeCatcher++;
} // incorrect handling
counter++;
} // while loop to determine if a student got a question right or wrong
static_cast <float> (incorrect); // float conversion
cout << endl; // for cleanliness
percent = ((static_cast <float> (correct)) / 20); // percentage
hundred = percent * 100;
PassFail(percent);
if (PassFail(percent) == "pass")
{
student++;
cout << "Report for Student " << student << ":" << endl;
cout << "-----------------------------" << endl;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
starter = 0;
while (starter < (sizeof catcher)
{
if(1=1)
{
catcher_size
}
else
{
cout << "";
starter++;
}
}
}
else if (PassFail(percent) == "fail")
{
student++;
cout << "Missed " << incorrect << " questions out of 20 for ";
cout << hundred << " % correct." << endl << endl;
while (starter < catcher_size)
{
if ((catcher[starter]) == -858993460)
{
starter++;
}
else
{
cout << "";
starter++;
}
}
}
return;
}
string PassFail(float percent)
{
if (percent >= 0.70) // if <pass>
{
return "pass";
}
else // if <fail>
{
return "fail";
}
cout << endl;
}
To get a loop you should keep streams open instead of closing them after reading 20 lines.
As pseudo code that would be:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter);
a = 0; // Reset a
}
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
You would also need to pass correct, incorrect, counter by reference so that the GradeReturn can change their value and their by do the accumulation.
Like:
void GradeReturn(char CAArray[], char SAArray[], int& correct, int& incorrect, int& counter)
Further you shouldn't rely on being able to read exactly Nx20 lines from the files every time. A file could have, e.g. 108 (5x20 + 8) lines, so you code should be able to handle the with only 8 lines. In other words, don't hard code 20 in your function like while (counter < 20). Instead pass the number of lines to be handled and do while (counter < number_to_handle).
Something like this as pseudo code:
a = 0;
while(streams_not_empty)
{
CA >> CAArray[a];
SA >> SAArray[a];
++a;
if (a == 20)
{
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
// ^
a = 0; // Reset a
}
}
if (a != 0)
{
// Process the rest
GradeReturn(&CAArray[counter], &SAArray[counter], correct, incorrect, counter, a);
}
CA.close(); // closing "CorrectAnswers.txt"
SA.close(); // closing "StudentAnswers.txt"
One problem you have is you're trying to compare C-style strings with the == operator. This will compare them essentially as if they were pointers to char, i.e. compare whether they point at the same location in memory, not compare the contents of the string. I urge you to look up array-decay and c-string variables to understand more.
Specifically, if (PassFail(percent) == "pass") isn't going to do what you want it to. strcomp doc, strncmp doc using std::string variables instead of c-style strings would all work, but it would be better simply to compare percent to a value, i.e. if(percent >= 0.70 directly instead of calling PassFail and comparing a string.
There are many other issues here also, you at one point call PassFail but do nothing with the return value. The only side affect of PassFail is cout << endl, if that's what you intend, it's a poor decision and hard to read way to put a newline on the console.
Try asking your compiler for more warnings, that's often helpful in finding these types of issues. -Wall -Wextra work for gcc, you may have to read your compiler manual...
I searched a lot on google and stackoverflow but I couldn't find my answer. I'm actually reading a C++ book (C++ Primer 5th Edition) and they're asking me to do an exercise.
"Write a program that reads several transactions and counts how many transactions occur for each ISBN" (Console Project)
This is my code atm :
Sales_item currentItem, item;
if (cin >> currentItem)
{
int cnt = 1;
while (cin >> item)
{
if (currentItem.isbn() == item.isbn())
{
++cnt;
}
else
{
cout << currentItem.isbn() << " occurs " << cnt << " times " << endl;
cnt = 1;
currentItem = item;
}
}
cout << item.isbn() << " occurs " << cnt << " times " << endl;
}
I won't explain how work the transactions so I'm gonna ask it in another way.
I type in my console 6(or more) random strings as exemple:
101A
102A
101A
101A
103A
102A
I want the result the result (output) to be:
101A occurs 3 times.
102A occurs 2 times.
103A occurs 1 times.
How would you do that?
Using std::map instead of list will be easier.
int main()
{
map<string,int> stringMap;
for (int i=0;i<3;i++)
{
cout<<"Enter string: ";
string s;
cin>>s;
if(stringMap.find(s)!=stringMap.end())
{
stringMap[s]++;
}
else
{
stringMap[s]=1;
}
}
for (map<string,int>::const_iterator itr = stringMap.cbegin(); itr!=stringMap.cend(); ++itr)
{
if(itr->second > 1)
cout<<itr->first << " occurs "<<itr->second<<" times"<<endl;
else
cout<<itr->first << " occurs "<<itr->second<<" time"<<endl;
}
return 0;
}
There are multiple approaches to a question like this, so the best would depend on your constraints. My approach would be:
while(GetInputString(str)) {
myStruct* ptr = existingList.Find(str);
if (!ptr) {
existingList.Add(str);
} else {
ptr->IncrementCount();
}
}
I've tried not to solve your problem for you - hopefully the answer gives you a template to work with...
#include <iostream>
#include <vector>
#include "Sales_item.h"
using namespace std;
void book_transactions(string isbn, int count);
int main()
{
vector<Sales_item> book_vec;
Sales_item book;
int total_transactions = 1;
// Reads in Sales_item objects
while (cin >> book)
{
book_vec.push_back(book);
}
// Compares ISBNs in vector, if two isbns are equal total transactions increases
for (int i = 0; i < book_vec.size() - 1; i++)
{
if (book_vec[i].isbn() == book_vec[i+1].isbn())
{
total_transactions = book_vec[i].get_units_sold() + book_vec[i+1].get_units_sold();
book_transactions(book_vec[i].isbn(), total_transactions);
}
}
}
void book_transactions(string isbn, int count)
{
cout << "ISBN: " << isbn << " " << "Transactions: " << count << endl;
}
I went into the Sales_item.h class and added a method to get the number of units sold. There was already a class member (unsigned return type) under private. I just created a getter and then created a vector of Sales_items. This solution seems to work. I'm sure the intended problem is not for an N set of sales_items, however this should work as a "duct-tape" solution. There is no need to play around with pointers at this point. A simple vector array and an algorithm to compare adjacent objects works just fine.
I am having trouble with getting a brute-force algorithm to work. As you can see from my code below, I am trying to evaluate a vector of structs and find the most efficient way to order a series of timed events. Here is my simple struct layout whose objects I am placing in the 'part1Vec' vector:
struct person
{
float swim;
float bike;
float run;
float bikeRun;
person();
person(float swim, float bike, float run)
{
this->swim = swim;
this->bike = bike;
this->run = run;
this->bikeRun = bike + run;
};
};
However, when I compile I get an error within the algorithm class that I have supposedly traced to this line:
while (next_permutation(part1Vec.begin(), part1Vec.end()))
the error message I get is this:
//Invalid operands to binary expression ('const person' and 'const person')
I believe I have the next_permutation function implemented correctly, but I do not understand why it is giving me this error. Any help would be appreciated, thanks. Here is my full code:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct person
{
float swim;
float bike;
float run;
float bikeRun;
person();
person(float swim, float bike, float run)
{
this->swim = swim;
this->bike = bike;
this->run = run;
this->bikeRun = bike + run;
};
};
//function declarations and global variables
vector<person> part1Vec;
vector<person> highestVec;
float highestRating;
float tempRating;
float rating(vector<person> vector);
int main()
{
//PART 1
//make objects
person one(20, 25, 20), two(35, 20, 15), three(40, 20, 30);
//insert into vector
part1Vec.push_back(one);
part1Vec.push_back(two);
part1Vec.push_back(three);
cout << "_________swim__bike__run__" << endl;
for (int i=0; i<part1Vec.size(); i++)
{
cout << "Vector #" << i+1 << ": "
<< part1Vec[i].swim << " "
<< part1Vec[i].bike << " "
<< part1Vec[i].run;
}
cout << endl << "Calculating..." << endl;
//Next permutation function
while (next_permutation(part1Vec.begin(), part1Vec.end())) //Invalid operands to binary expression ('const person' and 'const person')
{
//initialize highestVec
if (highestVec.size() == 0)
{
highestRating = rating(part1Vec);
highestVec = part1Vec;
}
//if Highest Rating is less than current permutation, update.
else if (highestRating < (tempRating = rating(part1Vec)) )
{
highestVec = part1Vec;
highestRating = tempRating;
}
}
cout << "Best Solution:" << endl;
for (int i=0; i<part1Vec.size(); i++)
{
cout << "Vector #" << i+1 << ": "
<< highestVec[i].swim << " "
<< highestVec[i].bike << " "
<< highestVec[i].run;
}
cout << endl << "Rating: " << highestRating << endl;
return 0;
}
float rating(vector<person> thisVector)
{
float rating = 0;
float swimSum = 0;
for (int i=0; i<thisVector.size()-1; i++)
{
swimSum += thisVector[i].swim;
if (rating < swimSum + thisVector[i].bikeRun)
rating = swimSum + thisVector[i].bikeRun;
}
return rating;
}
Kyle Y., did you understand what chris meant?
I hope Chris won't mind, but I'll take the liberty of elaborating just in case.
First up, say what compiler you're using. One reason is that they give different error messages. And in this instance, Invalid operands to binary expression ('const person' and 'const person') was kinda useful but not as useful as it could be. If I ran this through gcc, for example, it'd probably tell me something more like that std::next_permuation was looking for an undefined operator.
std::next_permuation uses a well ordering to generate permutations. So it needs arguments of types with an order defined on themselves, and so that the algorithm will always terminate, a consistent order (an ordering is inconsistent if a<(b) and b<(a) is possible, which is generally inadvisable regardless).
That's what chris is referring to, and the way you do that in C++ with say struct types like yours for which order is not already defined by a base class, is to override bool operator<(... in the struct.
Because you only need the ordering for your permutation generation, any old ordering will do as long as: everything is ordered and the ordering is consistent, as above.
See here for your code again with that override and a few unsigned ints in where they should be, note:
bool operator<(const person& rhs) const {
return (this->swim + this->bike + this->run) < (rhs.swim + rhs.bike + rhs.run);
}
Best.