So, I know some Python but I thought that I should try to add some c++ to that knowledge. This is the code I've written that I'm playing with (it's actually a rewrite of some python code). It adds some song data to a class (or a multidimensional array inside a class). So far so good. Most of it work. But I'm stuck at the delSong method. I don't think I could implement this with arrays, so It's seems like I'm at a dead end. I read a little about vectors, but they won't let med delete an item in the middle. How could I get further? Shall I abandon arrays for something else? But what would that be?
#include <iostream>
#include <string>
using namespace std;
class Jukebox{
public:
void addSong(string artist, string title, string filename) {
songs [songsCounter][0] = artist;
songs [songsCounter][1] = title;
songs [songsCounter][2] = filename;
songsCounter++;
}
void printSong (int song) {
cout << songs[song][0] << " - ";
cout << songs[song][1] << " : ";
cout << songs[song][2] << endl;
}
void printSongs () {
int song;
for (song=0; song<songsCounter; song++ ) {
cout << songs[song][0] << " - ";
cout << songs[song][1] << " : ";
cout << songs[song][2] << endl;
}
}
void delSong(int song) {
// Some code here
}
private:
int songsCounter;
string songs [512][3];
};
int main() {
Jukebox jbox;
jbox.addSong("U2", "Magnificent", "U2-Magnificent.mp3");
jbox.addSong("Sting", "Englishman in New York", "Sting-Englishman_in_New_York.mp3");
jbox.addSong("U2", "One", "U2-One.mp3");
jbox.printSongs();
return 0;
}
In my python code I actually using an in memory sqlite3 db. But I could as well write it with a simple list of tuples. And that is a similar approach to what I'm trying to do here. But an in memory sqlite database would also be a possible way for me to do this in c++. But it does'nt look very easy either to implement?
One more thing, I also want to add a sort method later so please have that in mind when you suggest a solution so I don't get to another dead end... :)
Vectors do allow deletion in the middle (look for the erase function), albeit not at the greatest speed.
I'd recomend using a vector of structures instead of a multidimensional array.
struct song {
string artist;
string title;
string filename;
};
And then in your Jukebox:
vector<song> songs;
The vector grows as needed and keeps track of its own size, contrary to the array.
For sorting, there's std::sort function in the <algorithm> header.
First, add a constructor
Jukebox()
{
//do initialization here
}
You can delete a middle item in a vector.
http://www.cplusplus.com/reference/stl/vector/erase/
vector<unsigned int> myvector;
// set some values (from 1 to 10)
for (i=1; i<=10; i++)
myvector.push_back(i);
// erase the 6th element
myvector.erase (myvector.begin()+5);
Another approach, with the least code modification would be to move all the songs FOLLOWING the song to be deleted to the left by 1 and then decrement songCounter.
//Assuming song 1 is the song at index 0 and songCounter points to the first
//empty element in the array
//ex: 1,2,3,4,5 becomes 1,3,4,5
void delSong(int song) {
if(song <= 0) return;
if(song < songCounter-1) //we don't want to delete song 500 if we only have 20
{
song--;
while (song != songCounter)
{
songs[song][0] = songs[song+1][0];
songs[song][1] = songs[song+1][1];
songs[song][2] = songs[song+1][2];
song++;
}
}
//this handles case of deleting the last song in addition to the above case
if(song < songCounter)
{
songCounter--;
}
}
Related
I have this message board, for sale, wanted, type of program below. Ive finally figured out how to read everything in, but need advice on how to compare items. As I read in the file, want to check the array to see if an item that is wanted(true) and mathces the name of an item already in the array and is for sale. If a match is found don't add that item, and delete the wanted item from the array, and shift.
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdlib>
#include <list>
using namespace std;
struct messageBoard {
string item;
bool forSale;
int price;
};
int main(){
messageBoard board;
const int arrayLength = 100;
std::list<messageBoard> arr;
int index = 0;
string filename;
ifstream words;
cout<<"Please enter the filename:";
cin>>filename;
words.open(filename);
if (words.fail()) {
cout << "file not found or something" << endl;
}else {
string word;;
while (getline(words, word)) {
int wordIndex = 0;
stringstream ss;
ss << word;
while (getline(ss, word, ',')){
if (wordIndex==0){
board.item = word;
}
else if (wordIndex==1&&word==" for sale"){
board.forSale = false;
}
else if (wordIndex==1&&word==" wanted"){
board.forSale = true;
}
else if (wordIndex==2){
board.price = atoi(word.c_str());
}
wordIndex++;
}
index ++;
arr.push_back(board);
}
}
words.close();
for(std::list<messageBoard>::iterator it = arr.begin(); it != arr.end(); it++) {
std::cout << "item: " << (it)->item << " bool: " << (it)->forSale <<"Price: "<<(it)->price << std::endl;
}
}
I won't write the code for you, but I'll tell you how I'd approach the problem.
First, I'd redefine arr as
std::list<MessageBoard> arr;
To add elements to the list, use list::push_back.
if an item that is wanted(true) and mathces the name of an item already in the array
Write a bool function that returns true if those conditions are met, else false.
Scan the list with std::find or std::find_if, using your function. If the search succeeds, the function returns an iterator pointing to the wanted item.
delete the wanted item
Use list::erase on the iterator. If you mess up, the result of deleting an invalid iterator is undefined, but likely your runtime library will let you know, loudly.
and shift
No need. The list length takes care of itself.
You might also consider std::set for efficient searches, or std::map<MessageBoard, int> to keep a count of identical elements.
By using the standard library containers, you elevate your logic from dealing with array elements by location, and move it closer to the problem of matching messages. You'll also write fewer loops.
If I may suggest, I'd call arr something like board or messages. Use the name to convey meaning not known to the compiler. Also, atoi is a bit out of place here. Since you're already in stringstream territory, you might as well extract the price that way.
stringstream sw(word);
sw >> arr[index].price;
HTH.
My program works for small files, but if I use large files (bible, Artamenes (longest novel)) it never finishes. The program keeps using more memory. It starts with 5mb and was up to over 350 in 7 hours. Is it because it is very inefficient or am I missing something?
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
struct Pair // create a struct for each word so it includes not only the word, but its count
{
string word; //the inputted word, eventually
unsigned int frequency; //count for each word
Pair(unsigned int f, const string& w) : frequency(f), word(w) {} //create constructor
bool operator <(const Pair& str) const //for sort
{
return (str.frequency < frequency);
}
};
string rmPunct (string word)
{
unsigned int position;
while ((position = word.find_first_of("|.,:;\"'!¡?¿/()^[]{}\\;-_*+")) != string::npos) //remove any punctuation, etc.
{
word.erase(position, 1);
}
return word;
}
string allLower(string word)
{
std::transform(word.begin(), word.end(), word.begin(), ::tolower); //convert any uppercase letters to lower case
return word;
}
int main()
{
vector<Pair> myVector; //Create a vector of structs so I have a dynamic array (can extend)
fstream dataFile; // create the file stream
string fileName; // necessary to find the file
cout << "Enter the file name: ";
cin >> fileName;
dataFile.open(fileName); // open the file in input mode only (no output for safeness)
string word; //will be each word from the file
while (dataFile >> word) // the >> imports each word until it hits a space then loops again
{
word = rmPunct(word);
word = allLower(word);
Pair *p = new Pair(1,word);
myVector.push_back(*p); // pushes each newly created struct into the vector
if (dataFile.fail())
break; //stop when the file is done
}
for (unsigned int i=0;i<myVector.size();i++) //this double for-loop finds each word that was already found
{
for (unsigned int j = i+1;j<myVector.size();)
{
if (myVector[i].word == myVector[j].word) //simple comparing to find where the extra word lies
{
myVector.at(i).frequency++; //increment the count
myVector.erase(myVector.begin()+j);//and... delete the duplicate struct (which has the word in it)
}
else
j++;
}
}
sort(myVector.begin(), myVector.end());
ofstream results;
results.open("results.txt");
if (myVector.size() >= 60) //outputs the top 60 most common words
{
for (int i=0;i<60;i++) {
double percent = ((double)myVector[i].frequency/(double)myVector.size()*100);
results << (i+1) << ". '" << myVector[i].word << "' occured " << myVector[i].frequency << " times. " << percent << "%" << '\n';
}
}
else //if there are not 60 unique words in the file
for (unsigned int i=0;i<myVector.size(); i++)
{
double percent = ((double)myVector[i].frequency/(double)myVector.size()*100);
results << (i+1) << ". '" << myVector[i].word << "' occured " << myVector[i].frequency << " times. " << percent << "%" << '\n';
}
results.close();
}
This loop:
for (unsigned int i=0;i<myVector.size();i++) //this double for-loop finds each word that was already found
{
for (unsigned int j = i+1;j<myVector.size();)
{
if (myVector[i].word == myVector[j].word) //simple comparing to find where the extra word lies
{
myVector.at(i).frequency++; //increment the count
myVector.erase(myVector.begin()+j);//and... delete the duplicate struct (which has the word in it)
}
else
j++;
}
}
walks your words n^2 times (roughly). If we assume your 5MB file contains half a million words, thats 500000 * 500000 = 250 billion iterations, which will take some time to run through [and erasing words will "shuffle" the entire content of your vector, which is quite time-consuming it the vector is long and you shuffle an early item]
A better approach would be to build a data structure where you can quickly search through, such as a map<std::string, int> words, where you do words[word]++; when you read the words. Then search for the most common word by iterating of words and saving the 60 most common words [keeping a sorted list of the 60 most common...]
You could also do something clever like min(60, words.size()) to know how many words you have.
You have a small memory leak in your program, and as the data you read gets larger so does the number of leaks.
The code causing the memory leak:
Pair *p = new Pair(1,word);
myVector.push_back(*p); // pushes each newly created struct into the vector
Her you dynamically allocate a Pair structure, copy the structure to the vector, and the completely ignore the original allocated structure.
There is really no need for any dynamic allocation, or even a temporary variable, just do
myVector.push_back(Pair(1, word));
And if you have a new compiler that have C++11, then just do
myVector.emplace_back(1, word);
That should help you with part of the problem.
The other part is that your algorithm is slow, really really slow for large inputs.
That can be solved by using e.g. std::unordered_map (or std::map if you don't have std::unordered_map).
Then it becomes very simple, just use the word as the key, and the frequence as the data. Then for every word you read just do
frequencyMap[word]++;
No need for the loop-in-loop comparing words, which is what slows you down.
To get the 60 most frequent words, copy from the map into a vector using std::pair with the frequency as the first member of the pair and the word as the second, sort the vector, and simply print the 60 first entries in the vector.
I am having trouble of understanding how to create an array of pointers to structures. I tried to look up similar examples and threads in the forum but I still cannot get my code to work! As a result, I believe I have written an ugly piece of code that I do not know where it is wrong and how to fix it.
Here is the code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Movie
{
string name;
int numawards;
int nomination;
int year;
};
void *readfile(ifstream &infile, int &n);
int main()
{
ifstream infile;
int n = 0;
infile.open("old_movies.txt");
Movie *oldmovies;
oldmovies = readfile (infile, n);
return 0;
}
//*function documentation//
void *readfile (ifstream &infile, int &n)
{
infile >> n;
Movie *movies;
movies = new Movie[n];
for (int i = 0 ; i < n ; i++)
{
infile >> movies[i]->year >> movies[i]->numawards >> movies[i]->nomination;
infile.ignore();
infile.ignore();
getline(infile, movies[i]->name);
cout << movies[i]->year << " " << movies[i]->numawards << " " << movies[i]->nomination << " " << endl << movies[i]->name<< endl; //the cout here is to test and see if the code works.
}
return movies;
}
The purpose of this code is to read a txt file that contains the movie name, how many awards, how may nominations, and what year it is produced, and then print it out using pointers. Here is what the file looks like:
2
1935 1 3
The Dark Angel
1935 4 6
The Informer
1935 1 8
the first 4 digits represents the year, the second one represents number of awards it has gotten, and the last digit represents the number of times it has been nominated to an award.
Anyway, I am stuck at this part and I am really clueless about what to do here. I just hope that this code is not that bad to a point where there are numerous things to be changed. Any help or advice would be greatly appreciated.
Let's look at what you have here:
Movie *movies;
movies = new Movie[n];
This allocates an array of Movie instances. To allocate an array of pointers dynamically, you need to change this to
Movie** movies;
movies = new Movie*[n];
Now inside the for loop, you need to allocate each Movie instance:
movies[i] = new Movie();
You should also change the readfile() to return a Movie** rather than a void* so that you don't have to use any casts later.
But do you really need an array of pointers? Why not just use an array of structs. This would avoid the extra level of indirection and make your code a little simpler.
This is my first time asking something on this site, so if I breach any sort of etiquette, let me know.
I am really new to linked lists and thought that implementing them using the list datatype would be pretty straightforward, but it seems I'm trying to do something weird with them.
I have created a list called "eventList" into which I want to put a series of structs called "entry", and then I wish to put several "eventList"s into an array called "processList".
My issue is that the bit of code
processList[pid].push_front(newEntry);
seems to give me an error. Is there something obviously wrong with what I'm doing?
Here is the code:
#include <iostream>
#include <fstream>
#include <list>
#include <string>
#include <array>
using namespace std;
struct entry {
int eTime;
string eType;
int pid;
};
int main(){
ifstream infile;
string comm;
int num;
entry newEntry;
list<entry> eventList;
array<list<entry>, 1024> processList;
infile.open("input00.txt");
if(infile.is_open()){
while(!infile.eof()){
int pid = -1;
string eTy;
int eTi;
infile >> eTy;
infile >> eTi;
if(eTy == "NEW"){
pid++;
cout << "DB: Process " << pid << endl;
}
else{
newEntry.pid = pid;
newEntry.eType = eTy;
cout << "Type: " << newEntry.eType << " | ";
newEntry.eTime = eTi;
cout << "Time: " << newEntry.eTime << endl;
processList[pid].push_front(newEntry);
//processList[pid] = eventList; <- realized that that wouldn't work fairly quickly
}
}
}
else {
cout << "Sorry, that file doesn't work. :[" <<endl;
}
cout << "Original Order:" << endl;
list<entry>::iterator p = eventList.begin();
while(p != eventList.end()){
cout << p->eType << " For " << p->eTime << "\n"; //This comes from http://www.cplusplus.com/forum/beginner/3396/
p++;
}
//cout << "Ordered By Time:\n";
//eventList.sort(); <- commented out, because I can't get it to work. :[
system ("PAUSE");
return 0;
}
I really love that this resource is available, and I hope that I can follow the logic of any answers that come this way. Apologies for the noobishness, and thanks for looking!
You are initializing pid inside the while loop. So no matter what you do, you will be referencing array[-1]. Talk to you're debugger, it will prove me right.
If eTy != "NEW" then pid remains equal to -1.
So,
processList[pid].push_front(newEntry);
will crash because the subscript is out of bounds ( -1 )
Despite how awesome linked lists are, don't use list (or std::list without the "using namespace std" bit). Use vector (std::vector).
std::list provides the same functionality as std::vector except:
list cannot be randomly accessed with the [] operator like vector
list being a double linked list it uses about 4 times the memory as vector for small structures
list is slower to access even in a sequential way than vector. This is due to vector being easier to cache because it's localized.
list is generally being slower than vector in every other aspect too.
The only "advantage" of list that inserting a new element does not invalidate the iterators. But vector indexes are more reliable than iterators any day, so you should never use iterators unless you absolutely need to.
and push_front builds a list in reverse order, you should usually use push_back.
I have an array of Student objects. I set the array length to 100, but it doesn't have 100 valid Student objects in it. I want to be able to iterate through the array and grab all the valid Student objects, then stop when I get to an array cell that doesn't have a Student object.
I have tried putting NULL into the array cell after the last Student, and then checking if (queriedStudents[i]) as well as if(queriedStudents[i] != NULL), but neither has worked for me.
What is the best way to find the end of the used part of my array?
Student *Welcome::queryStudents(int *queries) {
int query = 0;
Student *matchedStudents[100];
int matchedPos = 0;
while (queries[query] > 0) {
for (int i = 0; i < numStudents; i++) {
if (allStudents[i]->id == queries[query]) {
matchedStudents[matchedPos] = allStudents[i];
matchedPos++;
}
}
query++;
}
matchedStudents[matchedPos] = NULL;
return *matchedStudents;
}
And my code chunk trying to print out each Student's values:
int i = 0;
while (i < 100) {
if (queriedStudents[i]) {
cout << "ID:\t" << queriedStudents[i]->id << endl;
cout << "Name:\t" << queriedStudents[i]->name << endl;
cout << "Addr.:\t" << queriedStudents[i]->address << endl;
cout << "Phone:\t" << queriedStudents[i]->phone << endl;
} else {
i = 100;
}
i++;
}
You've got a bigger problem. You declare the array matchedStudents on the stack in the function queryStudents. When control passes out of that function, the array passes out of scope. If you're trying to use it later (by means of the pointer it returns, which was the first element of the array) then you're messing with deallocated memory, which will almost certainly lead to undefined behavior. It's as if you're visiting a house that has changed owners since you were last there; there's no telling what's changed, and if you wander around with your eyes closed you might get into trouble.
You can declare the array on the heap:
Student **Welcome::queryStudents(int *queries) {
Student **matchedStudents = new *Student[100];
...
return matchedStudents;
}
Or pass it in by reference:
void Welcome::queryStudents(int *queries, Student **&matchedStudents) {
...
}
Either way, you can then tackle the problem of how to indicate the end of valid pointers. Your method looks feasible, but bear in mind that as #JerryCoffin has pointed out, std::vector is available. Arrays are a pain, and the STL containers (such as vector) were made to take care of these grubby details for you. These days working with arrays serves almost no purpose except pedagogy; play with them until you understand the concepts, then use more advanced containers which are base on them.