Compare variables from map with vector in c++ - c++

So, I have an input
x=20
y=40
z=x+y
w=20+40+80
s1="str1"
s2=s1+w
I store variables without operators in a map as the variable name and its type like x int, y int, and etc.
map <string,string> finalMap
I split variables of each line into a vector of string for each line as tokens = {20,40,80}; tokens = {s1,w}, and etc.
vector<string> tokens;
I want to compare variables in tokens and finalMap, if I have already declared a variable of tokens in the map. For example z=x+y, x and y already declared in finalMap, I want to check if those x and y already declared in finalMap to get their dataType string or int. I use double for loop, but it doesn't work for some reason.
for(auto i=finalMap.begin(); i!=finalMap.end();i++){
for(int j=0; j<tokens.size(); j++){
if(i->first==tokens.at(j)){
tokens.at(j)==i->second;
}
}
}
I have trouble with the for loop above because when I check values after, it appears that it doesn't replace dataType from map.
for(int i=0; i<tokens.size()-1; i++){
if(checkType(tokens.at(i))!=checkType(tokens.at(i+1)))
return "undefined";
else
return checkType(tokens.at(i));
}
checkType() returns variable type of string, either it is string, int, or list. Please don't downgrade I am a new user if you need more details just let me know I will explain.
Here is the output:
s1:str
s2:undefined
w:int
x:int
y:int
z:undefined

Double equals == is comparison and not assignment.
if(i->first==tokens.at(j)) {
tokens.at(j) == i->second;
}
You meant to use single equals =
if(i->first==tokens.at(j)) {
tokens.at(j) = i->second;
}

Related

Key Value Pair implementation C

I have a .txt file that stores student names along with two of their best marks. If a student for some reason, i.e. dropping out of course, fails to pass a course, then no marks are recorded.
My file looks like this
Samuel= 90.5, 95.9
Bill= 25.2, 45.3
Tim
Anthony= 99.9, 12.5
Mark
Rob
Basically, Tim, Mark and Rob failed the course and hence their marks are not stored. Also to differentiate between a failed mark and a pass mark, I have used the = symbol. Basically, I want to store all the names into memory alongside their associated values.
This is my implementation, however it is flawed in the sense that I have declared a double *marks[2] array to store all six marks, when clearly it will only store 3. I am having trouble storing the values into the double array.
This is my code...
istream& operator>> (istream& is, Students& student)
{
student.names = new char*[6];
for (int i=0; i<10; i++)
{
student.names[i] = new char[256];
student.marks[i] = new double[2];
is.getline(student.names[i], sizeof(student.names));
for (int j=0; j < 256; j++)
{
if((student.names[i][j] == '='))
{
int newPos = j + 1;
for (int k = newPos; k < 256; k++)
{
student.names[i][k - newPos] = student.names[k];
}
}
}
}
}
How can I go about storing the values of the students with the valid marks? Please, no use of vectors or stringstreams, just pure C/C++ char arrays
You have a few options, you could use a struct like so
struct Record {
std::string name;
double marks[2];
};
And then stick that into something like std::vector<Record> or an array of them like
Records *r = new Records[1000];
You could also keep three different arrays (either automatically allocated or dynamically allocated or even std::vector), one to hold the name, two to hold the marks.
In each case you would just indicate a fail by some thing like the marks being zero.
Also, you can use
std::string name;
double first, second;
std::cin >> name;
if (name[name.size() - 1] == '=')
std::cin >> first >> second;
And this will parse the input like you want it to for a single line. Once you've done that you can wrap the whole thing in a loop while sticking the values you get into some sort of data structure that I already described.
Hope that gives you a few ideas on where to go!
Here's a strategy:
First of all you need to implement a struct to hold the key-value pair, I suggest the following:
struct Student {
char name[30];
double marks[2];
};
Note that you can give the dimension of the char array inside the struct if you know that the length will never be higher. (which is given here)
Now what you need is to know how many lines are in your ifstream, you could make a loop of is.getline() calls to get there. (don't forget to call is.clear() and is.seekg(0) when finished, to be at the beginning for the real loop)
When you know how many lines are in your ifstream you can use dynamically cast the Array of your struct with the actual length of your file:
Student * students = new Student[lineCount]; // line count of is
As you can see, there's no need to have a std::vector to hold the values. Consider that the getline() loop may be an overkill just to get the line count, alternatively you could give a length to Students at compile-time by making an array with a length that will never be overpassed.
(e.g. Student students[128];)
Now you need to parse the lines, i'd suggest you make a loop like the following (line by line):
// int parseLine ( char* line, char* name, double* marks ) { ...
bool hasMarks=false;
int iLine=0; // Line pos iterator
int iName=0; // Name pos iterator
char mk1Str[4]; // String buffer, Mark 1
char mk2Str[4]; // String buffer, Mark 2
while(line[iLine]!='\0')
{
if(line[iLine]=='=')
{
hasMarks=true;
name[iLine]='\0';
for(int iMark=0;iMark<4;iMark++)
{
mk1Str[iMark]=line[iLine+iMark+2];
mk2Str[iMark]=line[iLine+iMark+8];
// ^^ You can harcode the offsets (2,8) since they don't change
}
break;
}
name[iName++]=line[iLine];
iLine++;
}
Now what you need is to parse the marks to double values, for this you could use the atof() function that works with char*. The bool hasMarks helps you know if a student has defined marks, if not, you could define dummy values like -1 for the mark fields of your struct.
I think this works quite well for your case...

Assign std::vector address of another std::vector

I'm writing a program to balance chemical equations. The program works by taking the equation string, splitting it up into a std::vector with a size of two based upon the equal sign, then parses the left side separatedEquation[0] and the right side separatedEquation[1] into another set of std::vector's leftHalf and rightHalf respectively.
Problem
I have a function Equation::filterEquation that parses the separatedEquation for the element names. I want to use a temporary vector that points to the address of either leftHalf or rightHalf. I know this is probably confusing, but here's my code and what I'mt trying to do. I think I need to use pointers, but I've never had to use pointers before and am not efficient with them.
void Equation::filterEquation()
{
for(int i=0; i<separatedEquation.size(); i++) //i = index of separated equation
{
int index=0;
std::vector<std::string> equationHalf;
if(i==0)
equationHalf = leftHalf; //set equationHalf to the address of leftHalf
if(i==1)
equationHalf = rightHalf; //set equationHalf to the address of rightHalf
for (std::string::iterator it = separatedEquation[i].begin(); it!=separatedEquation[i].end(); ++it, index++)
{
//Elements are set up so that He = Helium, while H = Hydrogen. This separates the elements based upon upper and lowercae
bool UPPER_LETTER = isupper(separatedEquation[i][index]); //true if character is upperCase
bool NEXT_LOWER_LETTER = islower(separatedEquation[i][index+1]); //true if next is lowerCase
if (UPPER_LETTER)// if the character is an uppercase letter
{
if (NEXT_LOWER_LETTER)
{
std::string temp = separatedEquation[i].substr(index, 2);//add THIS capital and next lowercase
equationHalf.push_back(temp); // add temp to vector
}
else if (UPPER_LETTER && !NEXT_LOWER_LETTER) //used to try and prevent number from getting in
{
std::string temp = separatedEquation[i].substr(index, i);
equationHalf.push_back(temp);
}
}
}
}
}
In the general sense you would replace:
std::vector<std::string> equationHalf;
...
equationHalf = leftHalf // same for rightHalf
with
std::vector<std::string>* equationHalf;
...
equationHalf = &leftHalf // same for rightHalf
And then replace any instance of equationHalf. with equationHalf->.
Though, in your case, I might consider seriously reconsidering your design, for instance breaking out the code that operations on equationHalf into a function and passing it a reference to the vector to operate on such as void doStuff(std::vector<std::string> & equationHalf), then simply calling doStuff(leftHalf) and doStuff(rightHalf).

Assign values based on variable name in a string

Weird question and hard to word but lets say I have a 2 files that have a string of what double variables will appear in the file at the top and then the corresponding double variables, something like:
File1 =
A B C D E
1.2 3.4 4.5 5.6 7.8
File2=
B D E
9.8 7.6 5.4
and I have a struct of doubles
struct numb{
double A,B,C,D,E};
is it possible to read in the string in file 1 (A B C D E) and whatever the first value in the string is (A) assign it to the corresponding struct value numb.A.
So then the next file it will read in the first value of the string (B) and assign it to numb.B.
I realize this is possible with a bunch of if statements but I was wondering if there is an easier way. The hardest part is the string of variables will always be some combination of A,B,C,D,E. I am programming in C++ VS10
You can create a map with the string to parse as the key, and a pointer to member of the corresponding attribute of your structure as the value.
std::map<std::string, double numb::*> mapLetterToCorrespondingAttribute;
Then parse your file and assign the value to the corresponding member pointed to by the value in your map corresponding to the key being the letter you parsed.
Read this multiple times before you say you don't understand :D
A switch is probably the easiest way to do this.
void set_member(numb &n, char member, double value)
{
switch (member) {
case 'A':
n.A = value;
break;
case 'B':
n.B = value;
break;
// etc.
default:
// handle error
}
}
Declare an array of double in struct numb.
struct numb {
void setValue(char label, double val) { value[label-'A'] = val; }
double getValue(char label) const { return value[label-'A']; }
double value[5];
};
Then, you could perform:
numb n;
n.setValue('A', 1.2);
double A = n.getValue('A');
Read the two lines into std::vector<std::string> and then put them into a map in pairs:
std::vector<std::string> vars; // the split up first line
std::vector<std::string> values; // split up second line
std::map<std::string, double> mapping;
for (int i = 0; i < vars.size(); ++i) {
mapping.insert(std::make_pair(vars[i], boost::lexical_cast<double>(values[i]));
}
If you pre-populate the map mapping with sensible default values, this should be quite simple. Also, you can substitute the call to boost::lexical_cast<double> with any conversion method you like.

Working with strcmp and string array

I'm trying to eliminate extra elements in the string array and I wrote the code below. There seems a problem with strcmp function and string arrays. Strcmp doesn't accept the string array elements that way. Can you help me fix that? array3 is string array. I'm coding in C++ and What I want to do is like there are multiple "apple"s or "banana"s in the string array. But I only need one "apple" or one "banana".
for(int l = 0; l<9999; l++)
{
for(int m=l+1;m<10000;m++)
if(!strcmp(array3[l],array3[m]))
{
array3[m]=array3[m+1];
}
}
strcmp returns 0 on equality, so if (strcmp(s1,s2))... means "if the strings are equal then do this...". Is that what you mean?
First of all, you can use operator== to compare strings of std::string type:
std::string a = "asd";
std::string b = "asd";
if(a == b)
{
//do something
}
Second, you have an error in your code, provided 10000 is the size of the array:
array3[m]=array3[m+1];
In this line you are accessing the m+1st element, with m being up to 10000. This means you will eventually try to access the 10001st element, and get out of array bonds.
Finally, your approach is wrong, and this way will not let you remove all the duplicate strings.
A better (but not the best) way to do it is this (pseudocode):
std::string array[];//initial array
std::string result[];//the array without duplicate elements
int resultSize = 0;//The number of unique elements.
bool isUnique = false;//A flag to indicate if the current element is unique.
for( int i = 0; i < array.size; i++ )
{
isUnique = true;//we assume that the element is unique
for( int j = 0; j < result.size; j++ )
{
if( array[i] == result[j] )
{
/*if the result array already contains such an element, it is, obviously,
not unique, and we have no interest in it.*/
isUnique = false;
break;
}
}
//Now, if the isUnique flag is true, which means we didn't find a match in the result array,
//we add the current element into the result array, and increase the count by one.
if( isUnique == true )
{
result[resultSize] = array[i];
resultSize++;
}
}
strcmp works on Cstrings only so if you wanna use it I suggest you alter it to the following: strcmp(array3[l].c_str(),array3[m].c_str()) which makes the strings C Strings.
Another option would be to simply compare them with the equality operator array3[l]==array3[m] this would tell you if the strings are equal or not.
Another way to do what you're trying to do is just to put the array in a set and iterate over it. Sets don't take more than one string of the same content!
References:
More about strcmp :http://en.cppreference.com/w/cpp/string/byte/strcmp
And moreabout c_str: http://en.cppreference.com/w/cpp/string/basic_string/c_str
Regarding String Comparison: http://en.cppreference.com/w/cpp/string/basic_string/compare
C++ Sets http://en.cppreference.com/w/cpp/container/set

Printing the First Array in a Deque of Structs

I have a Deque that contains this kind of stucts.
struct New_Array {
array<array<int,4>,4> mytable;
int h;
};
In this stuct 2 different arrays may have same value of h.
deque<New_Array> Mydeque;
I also know how many different h are in the deque(the value of steps). And how many stucts are in the deque(Mydeque.size()).
I need to print one array for each h. Starting from h=0 till h=steps (steps is a known int value). Each array that is going to be printed must be the closer to the end of the deque.
I tried something like this:
void foo(deque<New_Array> Mydeque, int steps)
for(int i=0; i<steps; i++)
{
deque<New_Array>::iterator it;
it = find(Mydeque.begin(),Mydeque.end(),i);
PrintBoard(*it); // This if a function where you enter the New_Array struct
// and it prints the array
}
}
The above gives me : error C2679: binary '==' : no operator found which takes a right-hand operand of type 'const bool' (or there is no acceptable conversion)
Or something like this:
void foo(deque<New_Array> Mydeque, int steps)
for(int i=0; i<steps; i++)
{
deque<New_Array>::iterator it;
for(unsigned int j=0;j<Mydeque.size();j++)
{
it = find_if(Mydeque.begin(),Mydeque.end(),Mydeque[j].h==i);
PrintBoard(*it);
break;
}
}
The above gives me: error C2064: term does not evaluate to a function taking 1 arguments
EDIT: The deque is not sorted. For each h an array should be printed. This array should be the one that is at this moment closer to the end of the deque.
Remember the last value and skip:
assert(!Mydeque.empty());
int old_h = Mydeque[0].h + 1; // make sure it's different!
for (std::size_t i = 0, end != Mydeque.size(); i != end; ++i)
{
if (Mydeque[i].h == old_h) continue;
print(Mydeque[i]);
old_h = Mydeque[i].h;
}
Firstly, note that you declare your std::array on the stack, so the storage will also be on the stack. This means that iterating over the structure involves loading a (4*4+1)*int for each comparison. If this is performance-sensitive, I would suggest using std::vector since the load will be only of the outer vector pointer and the h when only comparing h.
struct New_Array {
vector<vector<int,4>,4> mytable;
int h;
};
Secondly, if you need to access these tables through their h values, or access all the tables with a given h at once, make it easier for everyone and store them as vectors in a map, or a sorted vector of vectors:
std::map<int,std::vector<New_Array> > rolodex;
rolodex[someNewArray.h].push_back(someNewArray);
If you construct this in-order, then the first item in each vector will be the one to print:
for(auto it : rolodex) {
vector<New_Array> tablesForThisH = it->second;
if(tablesForThisH.begin() != tablesForThisH.end())
PrintBoard(it->second[0]);
}
Since std:map stores (and iterates) its keys in ascending (I think) order, this will run over the different h values in ascending order. Again it will only need to load the stack-stored struct, which is just the h int and the vector header (probably 12 bytes, as mentioned in this question).
Forgive me if the code is wrong, my stl is a little rusty.
Loop through the deque, and insert all elements into a map, using h as the key. Since your set of h values seems to be sequential, you can use a vector instead, but testing whether an element has already been found will be more difficult.
The solution is :
void Find_Solution_Path(deque<New_Array> Mydeque, int steps)
{
for(int i=0; i<steps+1; i++)
{
for(int j=Mydeque.size()-1;j>-1;j--)
{
if (Mydeque[j].h==i)
{
PrintBoard(Mydeque[j]);
cout<<endl;
break;
}
}
}
}