Comparing items in an array while adding and deleting matches simultaneously - c++

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.

Related

Most common words in Text file never finished for large files

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.

Adding a linked list of structs to an array produces an error

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.

How to use single letters to form words

Hey I currently have this code. It gets the user to input strings into an array, with a limit of 5. I plan to use the array to then form words from the array. How can I achieve this?
const int row = 5;
char array[row];
char count = 0;
char letter;
while (count < 5)
{
cout << "Enter a letter: ";
cin >> letter;
array[count] = letter;
count++;
}
cout << "Letter inputed" << endl;
for (count = 0; count < 5; count++)
{
cout << array[count] << " " << endl;
}
system("pause");
Here's a hint to get you started on the right track: don't even consider using std::next_permutation unless this is something you'll only ever use once or twice (and probably not even then, because it's actually more complicated than doing the job right).
Using std::next_permutation, your function will be approximately N! times slower than necessary1 -- in the case of 5 letters, that'll be 120 times slower, and if you ever use longer words, it'll get worse very quickly (e.g., for 10 letters it's over 3.5 million).
Instead, start by pre-processing your dictionary. Instead of a std::set<std::string> of words, create an std::map<std::string, std::vector<string>> (or std::unordered_map, though English has few enough words that this probably won't make a huge difference). As you read in a word from the dictionary, create a sorted version of that string. Use that as the key, and push the original version of the word onto the vector for that key.
Then when you get a word from the user, sort it, look that up in the map, and the associated vector will contain every word (from your dictionary) that can be created from those letters.
1. If you use std::map instead of std::unordered_map, that should be something like N!/(log N), but N! grows so fast and log N grows so slowly that it the difference is negligible (if you get N large enough that log N = 3, N! will be so large that N!/log N computation steps...well, you start to get into questions of cosmology, like whether the universe will have died of heat death before then (to which the answer seems to be "yes, probably").
Here's a hint to get you started. There's a function in the standard library called std::next_permutation. Assuming you have a dictionary of words to check against, a possible solution could look like this:
std::sort(array, array + row);
do {
// Check if array is a word.
} while (std::next_permutation(array, array + row));
This will cycle through every permutation of letters. It's now up to you to verify that it is a valid word.
This solution uses an associative array to map from sorted letters of the word to the words having such sorted letters. It's thus possible to get an answer with one lookup in the map, which takes asymptotically O(log N) time, where N is a size of your dictionary.
Create a file named dic.txt. In case you're using Visual Studio it should be in the same directory as your *.cpp files. Put several words inside in a "word in a row" format. Try the following code:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;
int main() {
// Dictionary file is in a "word in a row" format
map< string, vector<string> > dictionary;
ifstream dictionary_file("dic.txt");
if (!dictionary_file.good()) {
cout << "File doesn't exist" << endl;
return 0;
}
string word;
while (dictionary_file >> word) {
string key = word;
sort(key.begin(), key.end());
dictionary[key].push_back(word);
}
// Read the letters
string letters;
cin >> letters;
if (letters.size() > 5) {
cout << "Too much letters" << endl;
return 0;
}
// Sort the letters
sort(letters.begin(), letters.end());
// Output the answers
vector<string> & ret = dictionary[letters];
for (size_t i = 0, ilen = ret.size(); i < ilen; ++i) {
cout << ret[i] << endl;
}
}
Mention that such a solution cares for a case your letters are in. In case you don't need it, you can add calls to strtolower function (got that name from PHP) before you add a word to your dictionary and before you sort your letters.
string strtolowers(string const & word) {
string ret = word;
transform(ret.begin(), ret.end(), ret.begin(), tolower);
return ret;
}
You'll need to add <cctype> header for this function to work.

Inputting elements of unknown type into a vector

I'm working on a program that takes elements from a user and sorts them. For this program, I have to use a vector as the size of the element list is unknown prior to user input. Our instructions were:
Write a program in C++ to implement sorting of a list of elements.
Elements can be of any type but will all be of same type, like all
integers or all floats or all chars or all strings (strings shall be
sorted like in a dictionary). You can implement any sorting algorithm
of your choice.
Ask the user how many elements will be there
Ask the user to input elements
Ask the user to choose the sorting order: ascending or descending or both
Print both input and output lists
User will not provide any information regarding the type of elements
I'm not very familiar with vectors (teacher basically skimmed topic in class) and my book isn't giving me a whole lot of information on the subject. The problem I'm running into is that I don't know the type of the element list until the user begins input. So far, I have tried:
creating a void type vector (obviously not allowed now that I've researched it, oops)
overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element (which seemed like my best option when I thought of it, except I need access to the vector after the function terminates, so that ended up being a no go, too)
#include <typeinfo> in program, finding the type of the first element, and then
creating a vector using vector<typeid(firstElement).name()> and honestly I'm not
sure why that didn't work, but it didn't.
Like I said I have EXTREMELY limited experience with vectors as this is my first time using them. I'm also a fairly new programmer so a lot of the research I've done on this has gone over my head. Any help that could be given in this would be GREATLY appreciated!
C++ is a language statically typed. It means that all the types should be determined during compilation: you cannot introduce new types when running the program.
creating a void type vector (obviously not allowed now that I've researched it, oops)
void is actually quite a strange type, mostly a placeholder for when you would expect a type (like a function return type) and have none to provide. void* is used as a pointer to an unknown type (mostly in C) but this is quite a hack, because the information about the original is discarded (as far as the language is concerned) so this causes issues to actually do things with the value so obtained.
overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element
#include <typeinfo> in program, finding the type of the first element, and then creating a vector using vector<typeid(firstElement).name()> and honestly I'm not sure why that didn't work, but it didn't.
Unfortunately neither is possible: since you cannot declare a variable without a type, what would be the type of firstElement to begin with ?
The problem you are describing is difficult in general. Basically it means that you will have to accept a string of characters, and then code a set of rules to determine how to interpret those characters. This is done generically by using a grammar to encode those rules; but grammars might way complicated for what is probably a simple task.
Let me put together a small example:
class Input {
public:
enum Type {
Int,
Double,
String
};
static Input Parse(std::string const& s);
Input(): _type(Int), _int(0), _double(0.0) {} // need to define a default...
Type type() const { return _type; }
int asInt() const {
assert(_type == Int && "not an int");
return _int;
}
double asDouble() const {
assert(_type == Double && "not a double");
return _double;
}
std::string const& asString() const {
assert(_type == String && "not a string");
return _string;
}
private:
Type _type;
int _int;
double _double;
std::string _string;
};
Obviously, the real challenge is to correctly Parse the input.
The idea is to use a set of rules, for example:
an int is composed exclusively of digits, optionally prefixed by -
a double is composed exclusively of digits, with at most one . and optionally prefixed by -
a string can be anything, therefore is our catch-all
Then we can write the recognition part of the Parse method:
static bool isInt(std::string const& s) {
if (s.empty()) { return false; }
// The first character may be among digits and '-'
char const first = s.at(0);
if (not isdigit(first) and first != '-') { return false; }
// Subsequent characters may only be digits
for (char c: s.substr(1)) {
if (not isdigit(c)) { return false; }
}
// Looks like it is an int :)
return true;
} // isInt
// Note: any int could be interpreted as a double too
static bool maybeDouble(std::string const& s) {
if (s.empty()) { return false; }
// The first character may be among digits, '.' and '-'
char const first = s.at(0);
if (not isdigit(first) and first != '.' and first != '-') { return false; }
// There may only be one dot
bool hasSeenDot = s.at(0) == '.';
// Subsequent characters may only be digits and a dot now
for (char c: s.substr(1)) {
if (not isdigit(c) and c != '.') { return false; }
if (c == '.') {
if (hasSeenDot) { return false; } // no second dot allowed
hasSeenDot = true;
}
}
// Looks like it could be a double
return true;
} // maybeDouble
static Input::Type guessType(std::string const& s) {
if (isInt(s)) { return Input::Int; }
// Test double after we ensured it was not an int
if (maybeDouble(s)) { return Input::Double; }
return Input::String;
} // guessType
And with the guessing logic together, finally the parse comes:
Input Input::Parse(std::string const& s) {
Input result;
result._type = guessType(s);
switch(result._type) {
case Input::Int: {
std::istringstream stream(s);
s >> result._int;
return result;
}
case Input::Double: {
std::istringstream stream(s);
s >> result._double;
return result;
}
case Input::String:
result._string = s;
return result;
}
// Unreachable (normally)
abort();
} // Input::Parse
Phew!
So ? Almost there. Now we need to determine how to compare two inputs. It's easy if they all have the same type, if not you will need to determine an arbitrary logic. You can transform an input Int in an input Double easily enough, but for string it's a bit weirder.
// define < for comparing two instance of "Input",
// assuming they both have the same type
bool operator<(Input const& left, Input const& right) {
assert(left.type() == right.type() && "Different Types!");
switch(left.type()) {
case Input::Int: return left.asInt() < right.asInt();
case Input::Double: return left.asDouble() < right.asDouble();
case Input::String: return left.asString() < right.asString();
}
} // operator<
And finally, the program:
int main(int argc, char* argv[]) {
// parse command line
std::vector<Input> inputs;
// by convention argv[0] is the program name, it does not count!
for (int i = 1; i != argc; ++i) {
inputs.push_back(Input::Parse(argv[i]));
// Detect that the type is the same as the first input
if (inputs.size() >= 2) {
if (inputs.back().type() != inputs.front().type()) {
std::cerr << "Please only use one type among Int, Double and String\n";
return 1; // non-0 is an error
}
}
}
// sort
std::sort(inputs.begin(), inputs.end());
// echo back to the user
for (Input const& i: inputs) {
switch(i.type()) {
case Input::Int: std::cout << i.asInt() << "\n"; break;
case Input::Double: std::cout << i.asDouble() << "\n"; break;
case Input::String: std::cout << i.asString() << "\n"; break;
}
}
// End of the program
return 0;
}
Of course as I don't know the types you wish to deal with.. I've decided an arbitrary set ;) However this should give you a skeleton to base yourself on.
Looking at the actual requirements of the problem as stated in the comments, I suggest you store all the inputs in an std::vector<std::string> and sort the vector using std::sort. So, instead of worrying about different types, you can specify the sorting logic depending on what you interpret the strings in your vector to represent. So
Implement sorting functions for strings depending on what the strings represent (more later)
store inputs as strings in a vector.
Determine which type the strings represent
select the sorting function based on this type
Sort the vector using std::sort and the appropriate sort function.
Concerning the sorting function, std::sort accepts a binary functor or function that applies a "less-than" comparison to two elelemts, so your functors or functions should look something like
bool foo(const std::string& rhs, const std::string& lhs) {
// implement the logic
}
Edit: Looking at more recent comments, it seems that the main purpose if the exercise might have been to implement sorting algorithms for different types. In that case, I would suggest following the approach taken by the C++ standard library, that is, to implement sorting in terms or a less-than comparison between two types, thereby decoupling the sorting logic from the types to be sorted. So you would want a template sorting function, templated on iterator type and comparison function/functor.
If you know what are the types the user may input, you can use templates and inheritance:
class Generic {
public:
virtual void process_input() = 0; // Handles the next input from user
virtual void process_output() = 0; // Processes the data inserted
};
template <typename T>
class HandleInput : public Generic {
private:
std::vector<T> storage;
public:
HandleInput(T first)
{
storage.push_back(first);
}
void process_input()
{
// do whatever you want
}
void process_output()
{
// do whatever you want
}
};
int main(int argc, char **argv)
{
// Get first input
Input i = input();
Generic *g;
// Instantiate the "right" generic with a switch
switch (i.type) {
case T:
g = new HandleInput<T>(i.value);
}
// Use Generic from here onwards
}
That's just an idea (Input cannot be implemented like that, you need to change that part with the logic that gets something from the user and determines its type), but it has the benefit of masking the type into a generic class, so you can factor your code around the interface provided by Generic.
Another idea (easier, probably) is using a std::vector<void*> and an enum that tells you what the type of the data stored in the vector is. When you need to process that data somewhere in the future, you can switch on the enum to appropriately cast the vector elements to the correct type and dispatch them to the appropriate code.
EDIT: another idea is to define a templatized function that takes the input and sorts the array using standard comparators:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/lexical_cast.hpp>
template <typename T>
void print_v(std::vector<T> &v)
{
typename std::vector<T>::iterator it;
for (it = v.begin(); it != v.end(); it++)
std::cout << *it << " ";
std::cout << std::endl;
}
template <typename T>
void sort_and_print(T first, size_t n, bool asc)
{
std::vector<T> v;
v.push_back(first);
for (size_t i = 0; i < n; i++) {
std::string s;
std::cin >> s;
T e = boost::lexical_cast<T>(s);
v.push_back(e);
}
print_v(v);
if (asc)
std::sort(v.begin(), v.end(), std::greater<T>());
else
std::sort(v.begin(), v.end());
print_v(v);
}
int main(int argc, char **argv)
{
std::string s = "test";
sort_and_print(s, 2, true);
unsigned int j = 3;
sort_and_print(j, 2, true);
return 0;
}
The logic to determine the type of the first input is up to you (maybe you can open another question) ;)
There are two aspects to this question: parsing & sorting.
You can use regular expressions to check the user-input data-types.
You can use cin to parse the data.
First: realise that you cannot necessarily know the type of your
users input until you have received all of it
~eg: consider a list of user names :
728278243
390349346
495045594
elizabeth
Hence, best not to assume to know best about the incoming data (can lead to a frustrating user-experience) but instead, prefer to treat everything as potentially a string. Store all raw input as strings so you can output in same format as input.
you can use say, an enumerated type to switch inside a sort comparator
or consider using a mutliset/multimap. here you will be
building an ordered set. so there is no need to sort.
NB: the complexity for constructing an ordered set of N elements or, for a single sort on N unsorted list elements, is roughly equivalent ~> NlogN
For your task-in-hand, it hardly matters but in reality depending upon upon how the list is used, one or other approach will be far more appropriate in performance terms.
If you have already used the likes of std::vector then std::multimap shouldn't be too scary. Loosely, it is an associated array of key-value pairs. the multi here meaning it can store multiple elements with the same key (which here, you want).
In this example i am using the boost regex library in order to determine some funky input data-types.
(eg: sudo apt-get install libboost-regex1.46-dev)
This regex might seem arcane but there are many examples on the i/web for practically every conceivable pattern.
[NB: C++11 regex is pretty-much a drop-in replacement for boost regex. ie: boost regex should be forward-compatible with the emerging C++11 standard]
blah.cpp:
#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <map>
#include <set>
#include <boost/regex.hpp>
//NB: GNU gcc added *experimental support for regular expressions in TR1 v 4.3.0.
// compile with: -std=c++0x
using namespace std;
using namespace boost;
//some example input data-types (perhaps notably missing a date!)
const regex re_char("[^0-9]", regex_constants::extended); //non numeric chars
const regex re_digit("[[:digit:]]+", regex_constants::extended); //a string of only digits in range [0..9] ~ie: Z+
const regex re_xdigit("0[xX][[:xdigit:]]+", regex_constants::extended); //support hex iff starts with '0x' or '0X'
const regex re_float("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?", regex_constants::extended); //all kinds of numbers
int main(int argc, char** argv)
{
int i, countc=0;
double d;
string str;
int element_count;
do
{
cout << "how many elements will there be? ";
if (cin >> element_count) break;
cin.clear();
cin >> str;
cout << "\033[A\033[2K" << flush;
}
while(13);
cin.ignore(128,'\n');
multimap<double, string> list_num;
multimap<double, string> list_fp;
//NB: below, by way of example, construction using the 'greater<int>' comparison class achieves _descending_ order
multimap<int, string, greater<int> > list_int;
list<string> list_str;
for (int next=0; next < element_count; next++)
{
cout << "\033[A\033[2K" << flush;
cout << "enter next element in list ["<< next+1 << "/" << element_count << "] : ";
getline (cin,str);
if (regex_match(str, re_xdigit))
{
//see all about manipulators here:
//http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/
stringstream(str) >> hex >> i;
list_int.insert(pair<int, string>(i, str));
list_num.insert(pair<double, string>(i, str));
}
else if (regex_match(str, re_digit))
{
stringstream(str) >> i;
list_int.insert(pair<int, string>(i, str));
list_num.insert(pair<double, string>(i, str));
}
else if (regex_match(str, re_float))
{
stringstream(str) >> d;
list_fp.insert(pair<double, string>(d, str));
list_num.insert(pair<double, string>(d, str));
}
if (regex_match(str, re_char)) countc++;
list_str.push_back(str);
}
cout << "\033[A\033[2K" << flush;
cout << "input: unsorted list:" << endl;
for (list<string>::iterator it=list_str.begin(); it!=list_str.end(); it++)
cout << *it << endl;
if (list_int.size() == element_count)
{
cout << endl << "output: sorted list of Z+ types:" << endl;
for (multimap<int, string>::iterator it=list_int.begin() ; it != list_int.end(); it++ )
cout << (*it).second << endl;
}
else if (list_fp.size() == element_count)
{
cout << endl << "output: sorted list of fp types:" << endl;
for (multimap<double, string>::iterator it=list_fp.begin() ; it != list_fp.end(); it++ )
cout << (*it).second << endl;
}
else if (list_num.size() == element_count)
{
cout << endl << "output: sorted list of numeric types:" << endl;
for (multimap<double, string>::iterator it=list_num.begin() ; it != list_num.end(); it++ )
cout << (*it).second << endl;
}
else //output as sorted strings ~but in _descending_ order, using reverse iterator, by way of example
{
list_str.sort(); //but best to use list_str.sort(greater<string>()); with forward iterators
cout << endl << "output: sorted list of " << (countc == element_count ? "non numeric char" : "string") << " types:" << endl;
for (list<string>::reverse_iterator it=list_str.rbegin(); it!=list_str.rend(); ++it)
cout << *it << endl;
}
return 0;
}
Example was compiled & run on Ubuntu. Commandline stuff:
$
$ lsb_release -d
Description: Ubuntu 11.10
$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
$ g++ --pedantic -oblah blah.cpp -lboost_regex
$ ./blah
input: unsorted list:
4.77
2.0e+2
-.3
11
0x10
output: sorted list of numeric types:
-.3
4.77
11
0x10
2.0e+2
$
NB: This is example code:
There are many optimisations that can be made here. You clearly
don't need so many stl containers as i am using.
I do not strictly deal with the direction of the sort (but show a couple of ways it may be achieved).
It might also be nice to encapsulate the type-specific functionality
in C++ objects; have a base class & then derived classes for each type you wish
to support ~but this homework right? -so probably not worth going over-board then
;)

modifiable arrays

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--;
}
}