How to swap and remove lines at output? - c++

I am trying to out put the GPA based on their value. Highest GPA on top and Lowest GPA below. And also output 2 out of 5 of my output. Below is my output. As you can see, the person with the most GPA (3.9) is all the way below. is it possible to output him at the top followed by the second highest, third, fourth and so on. I got these data off a txt file.
Also if possible, I also need to print out only 3 out of he five with highest to lowest GPA. So 3 outputs with highest on top to lowest below out of the 5.
Apologies for the terrible code. I am very weak in this but am trying my very best.
This is my txt file. the first column is first choice, second choice, third choice, name, gpa.
CC,DR,TP,Alex Kong ,3.8
SN,SM,TP,Marcus Tan,4
DR,TP,SC,Asta Goodwin,3.6
SC,TP,DR,Malcum Yeo,2.8
SN,SM,TP,David Lim,3.7
and so on...
This is my output.
CC Students:
Alex Kong 3.8 Choice 1
Damian Koh 3.7 Choice 1
Matt Wiliiams 3.3 Choice 1
Irfan Muhaimin 3.3 Choice 1
Muru Arun 3.9 Choice 1
This is the code I have so far.
void sortCC(){
ifstream File;
File.open("DSA.txt");
while (File.good()) {
string Name;
string GPA;
string First;
string Second;
string Third;
getline(File, First, ',');
getline(File, Second, ',');
getline(File, Third, ',');
getline(File, Name, ',');
getline(File, GPA, '\n');
vector <string> v;
v.push_back(First);
v.push_back(Second);
v.push_back(Third);
v.push_back(Name);
v.push_back(GPA);
if (First == "CC"){
cout << Name << " " << GPA << " " << "Choice 1" << '\n';
}
}
}
This is the kind of output I need.
Muru Arun 3.9 Choice 1
Alex Kong 3.8 Choice 1

Hakim, this isn't going to be fully trivial, but you'll learn a lot doing it. I'm going to outline what you need to do. You need to work on this as individual pieces.
First, you need to create a class that holds the individual records of data. The class would look something like this:
class Student {
public:
std::string first;
std::string second;
std::string third;
std::string name;
double gpa;
};
That's part one. Part two is to get data into it. Now, that's going to be from your input file, but for testing purposes, let's do it manually.
std::vector<Student> students;
Student firstStudent{"a", "b", "c", "First", 3.5};
students.push_back(firstStudent);
... Repeat until you have several of them.
Then you want a method that prints them, something like this:
std::ostream & operator<<(std::ostream &output, std::vector<Student> vec) {
for (const Student &student: vec) {
output << "Student: " << student.name << " GPA: " << student.gpa << endl;
}
return output;
}
You can call that method like this (for instance);
std::cout << students;
Try all that. It might take you a bit to get it right. Once you can create an array with sample data and print it out, you have only two more parts.
First, instead of hard-coding data, you'll need to read it from your input. You already kind of know how to do that. I think between what you know and the code above, you can figure this out.
Last, you have to sort it. Sorting is a little tricky. So I'm going to give you the line of code that will do it then explain how it works.
#include <algorithm> // Do this at the top of your .cpp
std::sort(students.begin(), students.end(),
[](const Student &first, const Student &second) {
return first.gpa > second.gpa;
});
There's a lot going on here. I'll explain it in parts.
std::sort() is part of the standard C++ library. It can sort based on natural order (which is great for numbers or strings), or you can pass in what's called a Comparator. We're doing it the second way.
You have to pass it the range of what you're sorting. (Unless you're on C++20, but you probably aren't.) That's what the students.begin() and students.end() part is -- we're telling it to sort the entire vector.
Finally, the comparator I've given you is called a lambda. You could also have written a comparison method and referenced it, but I do it this way. This is basically saying, "Here's the method that returns true if they're in the right order".
The syntax is weird, I know.
At this point, you just have to put it all together.
With this much help, it'll take you about another hour.

Related

How to split strings and pass them to class vectors from file

For my university class in programming we have been working on Object Oriented Programming (OOP) and are currently working on a group project. The project is to create a cash register that holds items with their names, amounts, and prices. As well as have a way to track the coins given by the user then determine the coin denomination. These are supposed to be done in different classes and involve objects.
My question is regarding the inventory manager that I am coding. The inventory manager is supposed to take the "data.txt" file and read it into the appropriate vectors. Currently I have a vector for the item name, price, amount, and then the itemList vector which holds a string of all 3 to print to the user for readability.
Here is a snippet from the data file:
20 1.99 Potato Chips
10 5.99 Ibuprofen
4 1.42 Candy
55 3.10 Coffee
12 3.25 Hummus
12 4.55 Guacamole
7 0.80 Baklava
45 1.50 Chocolate Chip Cookies
My question is, how do I split the line up so that I can pass the amount (first number) to the appropriate vector, pass the price to the appropriate vector, then pass the name to the appropriate vector. Essentially splitting each line into 3 parts. The part that is the most difficult to me is how the names of the items can be 1 word, 2 words, or even 3 words. Making it difficult to predict how many spaces the name will have, which caused a lot of my attempts to not work.
I found a working solution though I'm worried it's incorrect or inefficient and I'm curious in knowing the best way to do this. Thank you so much ahead of time and I will post the class definition and the method I'm working in down below.
< The stream object is inFile>
class inventoryManager
{
private:
double total, change, choice;
vector <int> amount;
vector <string> name;
vector <double> price;
vector <string> itemList;
public:
void fileOpen(fstream& inFile);
void fillInventory(fstream& inFile);
void printInventory(fstream& inFile);
};
void inventoryManager::fillInventory(fstream& inFile)
{
string temp;
string a, b;
while (!inFile.eof())
{
inFile >> a >> b;
amount.push_back(stoi(a));
price.push_back(stod(b));
getline(inFile, temp);
name.push_back(temp);
itemList.push_back(temp);
}
}
Attempted: I tried using the find() function to find each space then use the index to print the estimated indices to each side of the white space to the vectors. Though it was extremely messy and wouldn't work if a different data file was inputted. The idea of the program is to be able to work if a different file is put in, similar format but the amount could be 100, and prices could be more digits. I also tried editing the file directly by adding new lines for each space but ran into the item names with multiple spaces and ultimately didn't work.
You are trying to do too many new things at once. Break the problem into pieces and solve them separately, then combine them. To break the string into pieces you can use find and substr, you just have to be careful and debug until you're sure it's working perfectly:
string s = "45 1.50 Chocolate Chip Cookies";
cout << "s: " << s << endl; // for debugging
size_t first = s.find(' ');
cout << first << endl; // for debugging
string amountString = s.substr(0, first);
cout << amountString << "X" << endl; // for debugging
size_t second = s.find(' ', first+1);
cout << second << endl; // for debugging
string priceString = s.substr(first+1,second-first-1);
cout << priceString << "X" << endl; // for debugging
string nameString = s.substr(second+1);
cout << nameString << "X" << endl; // for debugging
The purpose of those X's is to be certain that the substring has no trailing space.
Once you have tackled the problem this way (and handed the result in for a grade), you can advance to tools like stringstream, and you won't have to deal with this grubby index arithmetic any more.

How to sort string array by number if the string contains numbers and characters c++ [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
essentially i have a function that writes a score to a text file, however i'm not sure how to write to a respective line in the text file based on the score so it writes to the bottom of the file on the next available line, and i also want a function to print the top 10 scores, however since that file is not sorted by score ( if there's an easy way to do that instead an answer for that is welcome ) my idea was to read all the lines in the file and put them into an array of strings, and then sort the array based on the numbers inside the array.
for example, the text file has the format of SCORE then NAME, here is an example of the file
1548 Bob Jones
604 James Jones
5516 Example Name
24 Bad Score ikr
to print this to the file i get input for the name from user, then i output to file with
HighScore << totalScore << " " << Name << std::endl;
i would like to be able to print the top 10 scores and the respective names of the player to the console, so the output would look something like this in the console
1) 5516 Example Name
2) 1548 Bob Jones
3) 604 James Jones
4) 24 Bad Score ikr
since my idea was to use arrays of strings, i still dont know how to sort it by the initial score numbers in the string, if there is a better solution to printing out the top 10 scores from a file then please let me know! thankyou
I suggest creating a class for keeping the score and name. You can then add operators for streaming objects of that class and a comparison operator. operator< is required by many standard functions and containers, so we'll add that. You can keep the scores you read in any container. I've used a std::set here which will keep the contents ordered at all times.
#include <algorithm> // std::copy, std::min
#include <iostream> // std::cin, std::cout
#include <iterator> // std::istream_iterator, std::ostream_iterator
#include <set> // std::set
#include <sstream> // std::istringstream
#include <string> // std::string
#include <tuple> // std::tie
class score_t {
public:
bool operator<(const score_t& s) const {
// sort on score in decending order
// if scores are equal, sort on name, ascending order
return std::tie(s.score, name) < std::tie(score, s.name);
}
friend std::istream& operator>>(std::istream& is, score_t& s) {
if(is >> s.score) {
is.ignore(1); // ignore space between score and name
std::getline(is, s.name); // read rest of line
}
return is;
}
friend std::ostream& operator<<(std::ostream& os, const score_t& s) {
return os << s.score << ' ' << s.name;
}
private:
int score{};
std::string name{};
};
int main() {
// test data
std::istringstream cin{
"1548 Bob Jones\n"
"604 James Jones\n"
"5516 Example Name\n"
"100 BBB\n"
"100 AAA\n"
"24 Bad Score ikr\n"};
// Use the new operator>> to read score_t's from a stream and put them in a
// set. std::set has a constructor taking iterators that can be used to populate the
// set directly. Since cin is a stream and not an iterator, we can use
// std::istream_iterator<score_t>(cin) to create an iterator to use as beginning.
// std::istream_iterator<score_t>{} creates an end iterator. Read to the end of file.
std::set<score_t> scores(std::istream_iterator<score_t>(cin),
std::istream_iterator<score_t>{});
// print the top 5 of the collected score_t's
// std::copy can copy a range of values given by iterators and insert them
// where a third iterator points.
// We copy from the beginning and then max 5 score_t's by creating the end
// iterator using std::next. The result is copied to an ostream_iterator
std::copy(scores.cbegin(),
std::next(scores.cbegin(), std::min(scores.size(), 5ul)),
std::ostream_iterator<score_t>(std::cout, "\n"));
}
Output:
5516 Example Name
1548 Bob Jones
604 James Jones
100 AAA
100 BBB
You can't use just one big string because 604 James Jones is lexicographically larger than 5516 Example Name. The first character of 604, 6, is greater than the 5 of 5516 in spite of 5516 being a larger number than 604.
Rather than an array of strings, I'd use a container, probably a std::vector, of a score structure. score would contain the score (possibly an int), the name (a string), and an operator< that could be used to arrange scores by their scores. Something like
struct score
{
int mScore;
std::string mName;
bool operator<(const score & other)
{
return mScore < other.mScore;
}
};
When parsing the file into the structure, use option 2 of this answer as inspiration.
You can then use std::sort from the algorithm library to sort the container.

I need help to properly write the fields of an object into a file in a consecutive manner

I'm working on a project that basically writes into a file the contents of some objects's fields data I've created(one of them being pers1, of the class PERSON);
I've inserted data into the field members of the object pers1, and I opened a file, trying to write the content of those field members(string name, surname and unsigned int age) into the file, using file.write function. It wrote parts of the contents, inbetween alot of garbage. Please help me write the proper code, so that I can write each person details into the file in a consecutive way. Thank you
#include<iostream>
#include<string>
#include<fstream>
#include <sstream>
#include <iomanip>
#include <list>
class PERSON
{
string name;
string surname;
unsigned int age;
public:
void inputinfo()
{
cin>>name;
cin>>surname;
cin>>age;
}
outputinfo()
{
cout<<name;
cout<<surname;
cout<<age;
}
};
class STUDENT: public PERSON
{
int ID;
float marks_sum;
string belonging_class;
public:
inputinfo()
{
cin>>name;
cin>>surname;
cin>>age;
cin>>ID;
cin>>marks_sum;
cin>>belonging_class;
}
};
void writeinfile()
{
PERSON pers1;
ofstream file1
file1.open("Students.txt", std::ofstream::out | std::ofstream::app);
pers1.inputinfo();
file1.write(pers1.c_str(),pers1.length()); // this is the second aproach I've found on internet, but it gives errors;
file1.write((char*)&pers1, sizeof(pers1)); // this one is puting alot of garbage into the file, alongside fields data.
fisier.close();
}
int main
{
int opt1, opt2;
char option;
switch(opt1)
{
case 1:
do
{
cout<<endl;
<<"Choose one of variants"<<"1.Students"<<"2.Teachers"<<"3.Get back to main menu"<<endl;
cin>>opt2;
switch(opt2)
{
case 1:
do
{
cout<<"Do you wish to introduce a new student(Y/N)?";
cin>>option;
if(option!='N')
writeinfile()
} while(option!='N');
break;
default:
cout<<"Incorect!"<<endl;
}
while(opt2!=3);
break;
case 2: "...."
;
break
case 3: "...."
;
break;
}
}
}
I expect clean write of field data into the file, everytime I call the aforementioned function.
For example for 1st iteration, when I enter data into the object's field: name : John, surname: Doe, age: 45, I espect to see this data into a single line in the file(and no garbage inbetween).
#include <iostream>
#include <fstream>
std::ostream& operator<< (std::ostream& os, const PERSON& value )
{
// here, you decide how the output format should look like. See below.
// ...
return os;
}
std::istream& operator>> (std::istream& is, PERSON& value )
{
// here, you do the reverse of what you chose in operator<<(). See below.
// ...
return is;
}
While you will be able to quickly hack an implementation to those 2 functions, it is worth while thinking of the requirements of what you want to accomplish:
Maintenance? What happens to your files in the future when you change your PERSON (e.g. extra fields)? Do you still want to be able to use those old files?
Robustness. Will you have to pay attention to localization? What will happen if your first Chinese Student arrives with a Kanji name? Do you need utf8 encoding or alike? Will you encounter "missing values"?
Scalability? Will you end up writing your own little data base with your own SQL to later on query for subsets of persons? Will you still be able to read the whole file once it has grown beyond expectations? Will new files with other data arrive and later there will be the need to associate them? (OUTER JOIN, INNER JOIN,...)
As you can see from this short and certainly incomplete list, you are at a cross road here: Use database instead? Use a standard serialization format like XML or JSON or protocol buffers or FastNProto or whatever is fashionable today? Or just go ahead and do your own thing as it is all a quick and dirty thing anyway?
Now, to the practical things:
Inside your operator<<, you can "dump" your elements like this (sticking to what you wrote in the question):
os << "name: " << value.name.c_str()
<< ", surname: " << value.surname.c_str()
<< ", age: " << value.age
<< std::endl;
And to read it back in, you implement the operator>> accordingly.
std::string tag;
is >> tag; // optionally make sure it is "name:"
is >> value.name;
// ... (The commas might need some fiddling... well you wanted help not a full solution...)
Then, all you need to do is test it, e.g. using a string stream, serialize a person, read it into another instance and compare. And you should be done ;)

Finding out lowest scoring students in each subjects, using C++/STL

Each student has got marks (out of 10) in different subjects. Below is sample csv file having student_name, subject, marks.
How can we write an efficient program using appropriate data structures to parse this file, process it and generate output? Our output should print who has got the lowest marks (and subject).
john, maths, 8
roy, science, 6
john, science, 5
sara, arts, 7
neil, maths, 4
tony, arts, 6
bob, science, 7
tony, maths, 7
neil, science, 8
john, arts, 4
sara, history, 3
Our output should print who has got lowest marks for any subject (as compared with all others in that subject). So our output should be:
neil
john
since neil got lowest marks in maths than others, john got lowest marks in science and arts than others. (However, sara will not appear in output because sara has not got marks in history lower than anyone else in that subject)
What have I done?
I tried using two maps in this way: <subject, <name, marks>>
i.e.
std::unordered_map<std::string, std::unordered_map<std::string, int>> name_subject_marks;
Here, I would add <name, marks> against each subject. And if the subject already exists I would replace associated <name, marks> only when existing marks are higher. Thus, finally I will get names of lowest marks for that subject.
However, sara is also appearing in this list. How can I handle it? Also please let me know if there is any better way to do this.
Use a boolean flag to mark the first person of each subject. Iterate over each line. Split the line. Store the marks and names with the flag in a map. Iterate over the map to insert all names in a set. Iterate over the set to print the names.
#include <iostream>
#include <unordered_map>
#include <set>
#include <sstream>
int main() {
std::string filename;
std::cin >> filename;
std::ifstream file(filename);
std::unordered_map<std::string, std::tuple<unsigned short, std::string, bool>> lowestMarks;
std::string line;
while (std::getline(file, line)) {
std::stringstream sline(line);
std::string name;
std::string subject;
unsigned short mark;
std::getline(sline, name, ',');
std::getline(sline, subject, ' ');
std::getline(sline, subject, ',');
sline >> mark;
auto lowestMark = lowestMarks.find(subject);
if (lowestMark == lowestMarks.end()) {
lowestMarks[subject] = std::make_tuple(mark, name, true);
} else if (std::get<0>(lowestMark->second) > mark) {
lowestMarks[subject] = std::make_tuple(mark, name, false);
} else {
std::get<2>(lowestMarks[subject]) = false;
}
}
std::set<std::string> names;
for (auto lm : lowestMarks) {
if (std::get<2>(lm.second)) continue;
names.insert(std::get<1>(lm.second));
}
for (auto name : names) {
std::cout << name << '\n';
}
return 0;
}
The code is tested with clang++ -std=c++11. The output is
john
neil

searching a name in the csv file on C++

I am a young programmer who is trying to learn c++. i have a working csv.file. but i want to search for a specific number assigned to the name and then displays the name of what i'm looking for. i have the file here:
1,Bulbasaur,grass
2,Ivysaur, grass
3,Venusaur, grass
4,Charmander, fire
5,Charmeleon, fire
6,Charizard, fire
7,Squirtle, water
8,Wartortle, water
9,Blastoise, water
Code
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream ip("pokedex.csv");
string pokedexnum[9];
string pokemonName[9];
string pokemonType[9];
cout<<"please enter a pokemon number:"<<" ";
cin>>pokemonType[0];
while (ip.good()){
getline( ip, pokedexnum[0]);
getline( ip, pokemonName[0]);
getline( ip, pokemonType[0]);
}
cout<<"the pokemon that is:"<< " "<<pokedexnum[0]<< "is the pokemon called:"<< pokemonName[0];
ifstream close("pokedex.csv");
return 0;
}
when it runs
please enter a pokemon number: 1
the pokemon that is: is the pokemon called:8,Wartortle, water
could you please point out what i am doing wrong?
Among the issues in this code:
You're not using std::getline correctly for comma-separated data. The result is each pass is consuming three lines from your input file; not three values from each line.
You're also not using ip.good() correctly as a while-condition.
You're retaining your test value in the array, which will be overwritten on the first iteration pass, so it is lost.
You're ignoring potential IO failures with each std::getline invoke.
You're overwriting slot-0 in your arrays with each loop iteration.
Minor, ifstream close("pokedex.csv"); clearly isn't doing what you think it is. That just creates another fstream object called close on the given file name.
The later may be intentional for now, but clearly broken in the near future.
In reality, you don't need arrays for any of this. All you're doing is reading lines, and seem to want to test the input number against that of the CSV data first column, reporting the line that you find, then ending this.
So do that:
Read the input value to search for.
Open the file for scanning.
Enumerate the file one line at a time.
For each line from (3), use a string stream to break the line into the comma separated values.
Test the id value against the input from (1). If the same, report the result and break the loop; you're done.
The result is something like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdlib>
int main()
{
std::cout<<"please enter a pokemon number: ";
long num;
if (std::cin >> num && num > 0)
{
std::ifstream ip("pokedex.csv");
std::string line;
while (std::getline(ip, line))
{
std::istringstream iss(line);
std::string id, name, skill;
if (std::getline(iss, id, ',') &&
std::getline(iss, name, ',') &&
std::getline(iss, skill))
{
char *endp = nullptr;
long n = std::strtol(id.c_str(), &endp, 10);
if (id.c_str() != endp && n == num)
{
std::cout << "The pokemon that is: " << num << " is called: " << name << '\n';
break;
}
}
}
}
}
Admittedly untested, but it should work.
Whether you want to store the items in arrays at this point is entirely up to you, but it isn't needed to solve the somewhat abstract problem you seem to be attempting, namely finding the matching line and reporting the name from said-same. If you still want to store them in arrays, I suggest you craft a structure to do so, something like:
struct Pokemon
{
int id;
std::string name;
std::string skill;
};
and have a single array of those, rather than three arbitrary arrays that must be kept in sync.
Four issues jump out at me:
You store the user's input into pokemonType, but then also use pokemonType for reading data from your CSV file. The file input is going to overwrite the user input.
Your file input loop always references index 0. All of the lines from your data file are going into element 0. That's the main reason that even if the user inputs 1, the output is from the last line of the data file.
Your file reading loop is structured like you want to put one part of each data line into a different array, but what you've written actually reads three lines on every iteration, storing those lines into the three different arrays.
This isn't affecting your output, but the code ifstream close("pokedex.csv"); is written like you want to close the file stream you opened, but I do believe what this line actually does is create a new ifstream called close, and opens pokedex.csv attached to it. In other words, it's just like your other line ifstream ip("pokedex.csv"); but with close as the variable name instead of ip.
You are going to want to look into something called "string tokenization". Start with some web searches, apply what you read about to your code, and of course if you hit another snag, post a new question here to Stack Overflow, showing (as you did here) what you tried and in what way it isn't working.
Elaborating on #3, here's what how your data file is being read:
at the end of the 1st iteration of the file-reading loop, ...
pokedexnum[0] is "1,Bulbasaur,grass"
pokemonName[0] is "2,Ivysaur, grass"
pokemonType[0] is "3,Venusaur, grass"
at the end of the 2nd iteration of the file-reading loop, ...
pokedexnum[0] is "4,Charmander, fire"
pokemonName[0] is "5,Charmeleon, fire"
pokemonType[0] is "6,Charizard, fire"
at the end of the 3rd iteration of the file-reading loop, ...
pokedexnum[0] is "7,Squirtle, water"
pokemonName[0] is "8,Wartortle, water"
pokemonType[0] is "9,Blastoise, water"
And that's why
<< "is the pokemon called:"<< pokemonName[0];
outputs
is the pokemon called:8,Wartortle, water