c++ multidimensional sorting - c++

I have written a basic program that collects student names and answers and automatically scores them. At the end I'd like to sort the scores in descending order with the corresponding names. I understand how to sort the scores but not in combination with the student names. This is what I have so far.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
//declare variables
char choice;
string studentName;
vector<char> answers;
vector<string> names;
int getStudents();
int getQuestions();
//calls function to get number of questions
int questions = getQuestions();
//Get answers
for (int i = 0; i < questions; ++i) {
cout << "What is the answer for question " << i + 1 << endl;
cin >> choice;
answers.push_back(choice);
}
//Get number of students
int students = getStudents();
//Get student names
for (int i = 0; i < students; i++) {
cout << "Student " << i + 1 << ", what is your name?" << endl;
cin >> studentName;
names.push_back(studentName);
}
int score = 0;
char studentAnswer;
vector<char> userAnswer;
vector<float> finalScore;
//gets student answers
for (int i = 0; i < students; i++) {
score = 0;
for (int j = 0; j < questions; j++) {
cout << names[i] << ", what is your answer for question " << j + 1 << "?" << endl;
cin >> studentAnswer;
userAnswer.push_back(studentAnswer);
if (userAnswer[i*questions+j] == answers[j])
score = score + 1;
}
finalScore.push_back(score);
}
//outputs scores
std::sort(finalScore.begin(), finalScore.end());
for (int i = 0; i < students; i++) {
cout << names[i] << " scored " << finalScore[i] << " out of " << questions <<
" or " << (finalScore[i] / questions) * 100 << "%" << endl;
}
system("pause");
return 0;
}
//function to get number of questions
int getQuestions()
{
int questions;
cout << "How many questions are there?" << endl;
cin >> questions;
return questions;
}
//function to get number of students
int getStudents()
{
int students;
cout << "How many students are there?" << endl;
cin >> students;
return students;
}
Right now, it sorts the scores in descending orders but the names that are output with the scores are incorrect.

You need to somehow 'connect' student name with his/her score. A very easy and straightforward way is to create a struct, i.e.
typedef struct Student
{
string student_name_;
float student_score_;
} Student;
Next you need to define a Compare function (look here for an example: http://www.cplusplus.com/reference/algorithm/sort/) so that you can use std:sort to sort a vector of Students. Your Compare function could like like this:
bool myCompareFunction ( Student a, Student b)
{
return (a.student_score_ < b.student_score_);
}

To keep the spirit of what you are doing, you can make a vector of pairs, here is a code example:
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
int main()
{
std::vector< std::pair<double,std::string> > my_student_list; // first element is the grade, second is the name
std::pair<double,std::string> student1(2.,"name1"),student2(2.,"name2"),student3(1.,"name3");
my_student_list.push_back(student1);
my_student_list.push_back(student2);
my_student_list.push_back(student3);
std::sort(my_student_list.begin(),my_student_list.end()); // sort by grade
// loop over the vector to print the sorted list
for(std::vector< std::pair<double,std::string> >::iterator iterator = my_student_list.begin(); iterator != my_student_list.end(); iterator++){
// iterator->second is the name, and iterator->first the associated grade
std::cout << iterator->second << " " << iterator->first << std::endl;
}
return 0;
}

Related

How do I convert from arrays to STL vectors?

In my class we recently got introduced to STL vectors. My professor has given us a program that uses arrays, and we are to convert it to use std::vectors instead. He would like us to use iterators, so we're not allowed to use square brackets, the push_back member function, or the at member function. Here's one of the for loops from the program I have to convert:
void readData(Highscore highScores[], int size)
{
for(int index = 0; index < size; index++)
{
cout << "Enter the name for score #" << (index + 1) << ": ";
cin.getline(highScores[index].name, MAX_NAME_SIZE, '\n');
cout << "Enter the score for score #" << (index + 1) << ": ";
cin >> highScores[index].score;
cin.ignore();
}
cout << endl;
}
`
I'm just not quite understanding how to convert them. so far, I was kind of able to get this: for (vector <Highscore> :: iterator num = scores.begin(); num < scores.end(); num++)for the for loop. It doesn't quite make sense to me so I was hoping I can get some more tips or even more information on how to convert them. I don't want an answer, simply just a tip. Thank you! (if its of any help, this is the program I am having to convert and these are the four headers we have to use
void getVectorSize(int& size);
void readData(vector<Highscore>& scores);
void sortData(vector<Highscore>& scores);
vector<Highscore>::iterator findLocationOfLargest(
const vector<Highscore>::iterator startingLocation,
const vector<Highscore>::iterator endingLocation);
void displayData(const vector<Highscore>& scores);
above are the headers that have to be used (having to use these instead of the programs headers)
#include <iostream>
using namespace std;
const int MAX_NAME_SIZE = 24;
struct Highscore{
char name[MAX_NAME_SIZE];
int score;
};
void getArraySize(int& size);
void readData(Highscore highScores[], int size);
void sortData(Highscore highScores[], int size);
int findIndexOfLargest(const Highscore highScores[], int startingIndex, int size);
void displayData(const Highscore highScores[], int size);
int main()
{
Highscore* highScores;
int size;
getArraySize(size);
highScores = new Highscore[size];
readData(highScores, size);
sortData(highScores, size);
displayData(highScores, size);
delete [] highScores;
}
void getArraySize(int& size){
cout << "How many scores will you enter?: ";
cin >> size;
cin.ignore();
}
void readData(Highscore highScores[], int size)
{
for(int index = 0; index < size; index++)
{
cout << "Enter the name for score #" << (index + 1) << ": ";
cin.getline(highScores[index].name, MAX_NAME_SIZE, '\n');
cout << "Enter the score for score #" << (index + 1) << ": ";
cin >> highScores[index].score;
cin.ignore();
}
cout << endl;
}
void sortData(Highscore highScores[], int numItems) {
for (int count = 0; count < numItems - 1; count++){
swap(highScores[findIndexOfLargest(highScores, count, numItems)],
highScores[count]);
}
}
int findIndexOfLargest(const Highscore highScores[], int startingIndex, int numItems){
int indexOfLargest = startingIndex;
for (int count = startingIndex + 1; count < numItems; count++){
if (highScores[count].score > highScores[indexOfLargest].score){
indexOfLargest = count;
}
}
return indexOfLargest;
}
void displayData(const Highscore highScores[], int size)
{
cout << "Top Scorers: " << endl;
for(int index = 0; index < size; index++)
{
cout << highScores[index].name << ": " << highScores[index].score << endl;
}
}
You maybe looking for one of two things.
If you want to add something to a vector, the function is push_back
vecScores.push_back(value) ; //in a for loop.
https://www.cplusplus.com/reference/vector/vector/push_back/
If you want to add something to a map, you could just use the form of
mapScore[index]=value ; // in a for loop.
https://www.cplusplus.com/reference/map/map/operator[]/
Probably your professor wants you to write something like this:
void readData(std::vector<Highscore>& highScores)
{
for (auto it = highScores.begin(); it != highScores.end(); ++it) {
cout << "Enter the name for score #" << std::distance(highScores.begin(), it) << ": ";
cin.getline(it->name, MAX_NAME_SIZE, '\n');
cout << "Enter the score for score #" << std::distance(highScores.begin(), it) << ": ";
cin >> it->score;
cin.ignore();
}
cout << endl;
}
where it is the iterator that's incremented via ++it from highScores.begin() to just before highScores.end(); then you access the members of the highScores's element "pointed by" it via it->member.
Here's a complete demo.
By the way, considering how much your professor likes void(some_type&) functions (and using namespace std;, if that was not your own idea), I would doubt you have much to learn from him. You better buy a good book.
I would do it like this, also get used to typing std::
Why is "using namespace std;" considered bad practice?
Also be careful with signed/unsigned, be precise about it.
If something can't have a negative value use unsigned types (or size_t)
#include <iostream>
#include <string>
#include <vector>
struct HighScore
{
std::string name;
unsigned int score;
};
// use size_t for sizes (value will always >0)
std::vector<HighScore> GetHighScores(size_t size)
{
std::vector<HighScore> highScores;
std::string points;
for (size_t index = 0; index < size; index++)
{
HighScore score;
std::cout << "Enter the name for score #" << (index + 1) << ": ";
std::cin >> score.name;
std::cout << "Enter the score for score #" << (index + 1) << ": ";
std::cin >> points;
// convert string to int
score.score = static_cast<unsigned int>(std::atoi(points.c_str()));
highScores.push_back(score);
}
std::cout << std::endl;
return highScores;
}
int main()
{
auto highScores = GetHighScores(3);
return 1;
}

Using arrays and strings together

So I'm trying to create an array that contains some user inputted names, and then associate those names with letter grades from tests (ex: A, B, C, D, F). My question is, how would I use an array to accept the user inputted names?
EDIT:
Sorry this is a bit long, I don't know what part to put that would help out. Totally new to C++ and I can't seem to find anything online regarding the matter, lol.
Here is some code. This program currently asks the user for test scores, then displays and drops the lowest test score, and finally, calculates the average of the scores without the lowest one. The end goal is to ask the user for 5 students names, and 4 scores for each student, then dropping the lowest score for each student and calculating the averages of ALL scores inputted regardless of student.
#include <iostream>
#include <string>
using namespace std;
void getScore(int &);
int findLowest(int [], int);
void calcAverage(int [], int);
int main () {
const int NUM_SCORES = 5;
int scores[NUM_SCORES];
cout << "Welcome to test averages." << endl;
cout << "Please enter scores for " << NUM_SCORES << " students." << endl;
cout << endl;
for (int i = 0; i < NUM_SCORES; i++) {
getScore(scores[i]);
}
for (int i = 0; i < NUM_SCORES; i++) {
cout << "Score " << (i + 1) << ": " << scores[i] << endl;
}
cout << endl;
cout << "The lowest of these scores is " << findLowest(scores, NUM_SCORES) << endl;
calcAverage(scores, NUM_SCORES);
return 0;
}
void getScore(int & s) {
s = -1;
cout << "Please enter a test score: ";
cin >> s;
while (s < 0 || s > 100) {
cout << "Score range must be from 0-100" << endl;
cout << "Please re-enter a score: ";
cin >> s;
}
}
int findLowest(int theArray [], int theArraySize) {
int lowest = theArray[0];
for (int i = 1; i < theArraySize; i++) {
if (theArray[i] < lowest) {
lowest = theArray[i];
}
}
return lowest;
}
void calcAverage(int theArray [], int theArraySize) {
int sum = 0;
for (int i = 0; i < theArraySize; i++) {
sum += theArray[i];
}
double average = (sum - findLowest(theArray, theArraySize)) / (theArraySize - 1.0);
cout << "The average is " << average << endl;
}
Try getline from #include <string>
std::string names[5];
for (int i = 0; i < 5; ++i){
getline(std::cin, names[i]);
}

Is there a way to link input from two different arrays?

I'm having an issue with some school work where I need to create two arrays one for names and one for scores which allows the user to input into both arrays (i.e. Enter the players name:; Enter the players score:). Then I need to print the arrays in descending score and then ascending alphabetical. As a hint we were told: Using the string sort function combine the two arrays into one then sort.
However I can't figure out how to link the two values to one another so that if I enter Nathan with a score of 87 the two values can't be split apart.
This is what I have so far (with some things I was trying to get to work but couldn't):
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string names[10];
int scores[10];
string combine[20];
int count = 0;
while (count < 10){
cout << "Please enter a player's name: ";
cin >> names[count];
cout << "Now enter that player's score: ";
cin >> scores[count];
count++;
}
/*sort(begin(names), end(names));
sort(begin(scores), end(scores));*/
for (int i = 0; i < 10; i++){
cout << names[i] << ": " << scores[i] << "\n";
}
system("pause");
}
You want to "link" them from the very beginning:
struct Student {
string name;
int score;
bool operator<(const Student& rhs) const {
return score > rhs.score || (score == rhs.score && name < rhs.name);
}
};
That way, the sort is easy:
sort(begin(students), end(students));
Otherwise, you'd have to make an array of indices:
int indices[10];
std::iota(begin(indices), end(indices), 0);
And sort that:
std::sort(begin(indices), end(indices), [&](int a, int b){
return scores[a] > scores[b] ||
scores[a] == scores[b] && names[a] < names[b];
});
And then print according to indices:
for (int idx : indices) {
std::cout << names[idx] << " with score " << scores[idx] << '\n';
}
As far as combining the two arrays into one you can do something like this:
// create a struct that of type "result"
// that combines both name and score
struct result
{
string name;
int score;
};
int main()
{
string names[10];
int scores[10];
// array of your struct - same number of elements (10 not 20)
result combine[10];
int count = 0;
while (count < 10){
cout << "Please enter a player's name: ";
cin >> names[count];
cout << "Now enter that player's score: ";
cin >> scores[count];
count++;
}
/*sort(begin(names), end(names));
sort(begin(scores), end(scores));*/
for (int i = 0; i < 10; i++){
cout << names[i] << ": " << scores[i] << "\n";
}
// combine into one array
for(int i = 0; i < 10; ++i)
{
combine[i].name = names[i];
combine[i].score = scores[i];
}
// Now sort the combined array
system("pause");
}

Dynamically Allocated Structures

So i am having troubles here. The program works perfectly fine when i enter in 1 for numStudents but get a segmentation fault: 11 when i enter anymore that 1 for numstudents. Am i doing something wrong with the dynamic allocation? I am just lost have done everything i can think of.
#include <iostream>
#include <string>
#include <cstdlib>
#include <iomanip>
using namespace std;
//Structure Declaration
struct Student
{
string name;
long long ID;
double *score;
};
//Function prototypes
void calcAvg (int loc, Student test[], double average[], int tests);
int main()
{
int numStudents, numTests; //Get from user
double *averages; //For Dynamic allocation of averages
Student *info; //For Dynamic Allocation
cout << "Enter the number of students you will enter ";
cin >> numStudents;
info = new Student[numStudents];
averages = new double[numStudents];
cout << "\nEnter the number of tests that were taken by the students ";
cin >> numTests;
info->score = new double[numTests];
for(int s = 0; s < numStudents; s++)
{
cout << "Enter student #" << (s+1) << "'s name ";
cin.ignore();
getline(cin, info[s].name);
cout << "Enter " << info[s].name << "'s ID number ";
cin >> info[s].ID;
cout << endl;
for(int t = 0; t < numTests; t++)
{
cout << "\nEnter " << info[s].name << "'s score for test #" <<(t+1) << " ";
cin >> info[s].score[t];
while(info[s].score[t] > 100 || info[s].score[t] < 0)
{
cout << "The score you entered is invalid, try again. ";
cin >> info[s].score[t];
}
}
calcAvg(s, info, averages, numTests);
}
return 0;
}
void calcAvg (int loc, Student test[], double average[], int tests)
{
double total = 0;
for(int i = 0; i < tests; i++)
{
total += test[loc].score[i];
}
average[loc] = total/tests;
cout << average[loc] << endl;
}
You need to repeat this for each student
info->score = new double[numTests];
So you could move it into the loop:
for(int s = 0; s < numStudents; s++)
{
info[s].score = new double[numTests];
...
}
But all this is very error prone - I suggest you look into structures that can handle all this for you like std::vector.

Entering data from .txt into struct array

For my c++ homework, I have an .txt document containing building information ordered like:
Building name
year built
lat coordinate
lon coordinate
ex.
Parking Deck
1993
34.2252
37.5563
Admin Building
1985
34.2356
37.5734
I have to read this into an array of my created struct:
struct list
{
char name[50];
int yearBuilt;
double latCoord;
double lonCoord;
} building;
now I've created a for loop to read in the data to my created array of type list:
list buildingnumber[SIZE]; //array for buildings
But when I try to print out the "k" earliest buildings made, it shows no data in the array
Here's my current code:
#include <iostream>
#include <fstream>
#include <istream>
#include <cstdlib>
#include <string>
using namespace std;
const int SIZE = 5000;
//struct for building type
struct list
{
char name[50];
int yearBuilt;
double latCoord;
double lonCoord;
}building;
list buildingnumber[SIZE]; //array for buildings
void InsertionSort(list buildingnumber[], int buildingsloaded)
{
int key = 0, i = 0;
for(int j = 1; j < buildingsloaded; j++)
{
key=buildingnumber[j].yearBuilt;
i=j-1;
while(buildingnumber[i].yearBuilt > key && i >= 0)
{
buildingnumber[i+1] = buildingnumber[i];
i--;
}
buildingnumber[i+1].yearBuilt = key;
}
}
int main()
{
char filePath[50];
ifstream openFile;
cout << "Enter the path of the building file: ";
cin.getline(filePath, 50);
openFile.open(filePath);
//verify if file is opened + report buildings loaded
int buildingsloaded = 0;
if(!openFile.fail())
{
while(openFile >> building.name >> building.yearBuilt >> building.latCoord >> building.lonCoord)
{
buildingsloaded++;
}
cout << buildingsloaded << " buildings have been loaded." << endl;
}
// get how many buildings user wants
int k = 0;
cout << "How many buildings are you interested in?: ";
cin >> k;
//create array
// loadKbuildings(building, buildingsloaded);
for(int i = 0; i < k; i++)
{
openFile >> buildingnumber[i].name >> buildingnumber[i].yearBuilt >> buildingnumber[i].latCoord >> buildingnumber[i].lonCoord;
}
// insertion sort
InsertionSort(buildingnumber, buildingsloaded);
// display earliest k buildings
cout << "The " << k << " oldest buildings are: " << endl;
int i = 0;
while ( i < k )
{
cout << buildingnumber[i].name << endl;
cout << "Year Built: " << buildingnumber[i].yearBuilt << endl;
cout << "Coordinates: (" << buildingnumber[i].latCoord << "," << buildingnumber[i].lonCoord << ")" << endl;
cout << endl;
i++;
}
}
The problem is here:
if(!openFile.fail())
{
while(openFile >> building.name >> building.yearBuilt >> building.latCoord >> building.lonCoord)
{
buildingsloaded++;
}
cout << buildingsloaded << " buildings have been loaded." << endl;
}
You've read all of the data already (to count the number of buildings); the next time you try to grab the data, it's gone.
You can resolve this issue by storing the buildings in an array the first scan through.
int c = 0;
if(!openFile.fail())
{
// *
while(openFile >> buildingnumber[c].name >> buildingnumber[c].yearBuilt >> buildingnumber[c].latCoord >> buildingnumber[c].lonCoord)
{
buildingsloaded++;
c++;
}
cout << buildingsloaded << " buildings have been loaded." << endl;
}
As per WhozCraig's comment, the line under the star only reads in one word for the building's name; you should use cin.getline() instead and change around the loop condition.
You should obviously also take out the data reading section below:
//create array
// loadKbuildings(building, buildingsloaded);
for(int i = 0; i < k; i++)
{
openFile >> buildingnumber[i].name >> buildingnumber[i].yearBuilt >> buildingnumber[i].latCoord >> buildingnumber[i].lonCoord;
}