C++ - Last value in array gets duplicated in map - c++

I'm trying to create a dictionary of words from a sentence using a map and an array of strings. My problem is every time I loop through to add values to the map from my array the last item always gets duplicated as a key. This is all on xCode by the way.
int main(){
char input[50];
int i = 0,
arr_size = 0;
string s[20];
cout << "Please input a phrase: ";
cin.getline(input, 50);
while(i < sizeof(input) - 1)
{
input[i] = tolower(input[i]);
if(isspace(input[i]) || ispunct(input[i]) )
arr_size++;
else
s[arr_size].operator+=(input[i]);
i++;
}
map<string, int> dictionary;
for (i = 0; i < arr_size; i++) {
if(dictionary.map::find(s[i]) == dictionary.end())
dictionary.insert(make_pair(s[i], 1));
else
dictionary.at(s[i])++;
}
typedef map<string, int>::const_iterator MapIt;
for (MapIt iter = dictionary.begin(); iter != dictionary.end(); iter++)
{
cout << iter->first << " : " << iter->second << endl ;
}
}
With my output looking something like this:
Please input a phrase: thE Blue Black BluE Cat Cat BLacK hat zap zap
black : 2
blue : 2
cat : 2
hat : 1
the : 1
zap : 1
zap : 1

The two "zap"'s are not the same. Probably the last one contains a newline character, '\n'. More generally, you need to learn how to index loops properly.

So it turns out while I was looping through
while(i < sizeof(input) - 1)
{
input[i] = tolower(input[i]);
if(isspace(input[i]) || ispunct(input[i]) )
arr_size++;
else
s[arr_size].operator+=(input[i]);
i++;
}
I didn't check for any other characters besides punctuation and space so it was reading in everything else.
for(int i = 0; i < sizeof(input) - 1; i++)
{
if(isspace(input[i]) || ispunct(input[i]))
arr_size++;
else if (isalpha(input[i])){
input[i] = tolower(input[i]);
s[arr_size].operator+=(input[i]);
}
}
This code worked out better and is indexed a little better.

Related

What is wrong with my program to find the longest word in a sentence?

#include <iostream>
using namespace std;
int main() {
char a[101]{0};
cin>>a;
cin.getline(a,101);
cin.ignore();
int currLen{0};
int maxLen{0};
int startInd{-1};
int endInd{-1};
for(int i=0; i<101; i++) {
if(a[i]!=' ' ) {
++currLen;
} else if(a[i]==' '||a[i]=='\0') {
if(currLen>maxLen) {
maxLen=currLen;
startInd=i-currLen;
endInd=i-1;
}
if(a[i]=='\0')
break;
currLen=0;
}
}
cout<<maxLen<<endl;
if(startInd==-1)
cout<<-1;
else
for(int i=startInd; i<=endInd; i++)
cout<<a[i];
return 0;
}
If I take an input here, for example, "My name is Manav Kampani"
It will output 5
Manav instead of 7
Kampani
But if I write "My name is Manav Kampani ", with space after the last word
than it is considering Kampani too printing Kampani.
Also when I input "Kampani Manav is my name" then too it's displaying the wrong output. That means it is not considering the first word of the sentence.
if(a[i]!=' ' )
{
++currLen;
}
else if(a[i]==' '||a[i]=='\0')
{
....
}
Consider the case of a[i] == 0. Which of these if-statements will apply.
Answer: the first one. Which means you'll never look at the final word in the string. You also don't exit at the end of the string, but instead loop through whatever is in your string all the way out to character 101.
As a general structure, be very, very careful with this:
if (condition)
else if (condition)
// without a final else section
If you do that, you need to think about what you're doing. In this particular case, you can have:
if (a[i] != 0 && a[i] != ' ')
else
It may not solve all your issues, but it should solve some.
A nice sliding window pattern implementation.
You have 3 problems in your code
You must not write cin >> a;
You must not write cin.ignore();
You need to modify your if statement like so: if (a[i] != ' ' && a[i] != '\0') Otherwise you will not detect the last word.
Your complete working code with that minor fixes will lokk like that.
int main()
{
char a[101]{ 0 };
//cin >> a;
cin.getline(a, 101);
//cin.ignore();
int currLen{ 0 };
int maxLen{ 0 };
int startInd{ -1 };
int endInd{ -1 };
for (int i = 0; i < 101; i++)
{
if (a[i] != ' ' && a[i] != '\0')// Add comparison
{
++currLen;
}
else if (a[i] == ' ' || a[i] == '\0')
{
if (currLen > maxLen)
{
maxLen = currLen;
startInd = i - currLen;
endInd = i - 1;
}
if (a[i] == '\0')
break;
currLen = 0;
}
}
cout << maxLen << endl;
if (startInd == -1)
cout << -1;
else
for (int i = startInd; i <= endInd; i++)
cout << a[i];
return 0;
}
Additionally. You should not use C-Style arrays in C++. And please use std::string
There is a couple of things here:
1- You don't need to do a cin>>a this is actually consuming the first word, and afterwards the content is overrided by cin.getline(). So removing the firsst cin>>ayou'll be fine.
2- The last word is not read because there isn't any if condition that matches the condition aka.
if(a[i]!=' ' ) case of not a space
//not end of word
else if(a[i]==' '||a[i]=='\0') case of space or null
//end of word
So your last character is not a space nor null, that means you don't detect the last word.

How to deduplicate a string where it only deletes consecutive duplicates

The goal of the program is to take a string like "kcck" and delete the consecutive duplicates. It should first iterate through the string and delete cc leaving kk; then go through again and delete kk; then return "empty" since there are no characters left in the string.
Another example, "aabggtcc" should return "bt".
int i;
int j = i+1;
string deduplicate(string input) {
for(i=0; i<input.length(); ++i) {
while(j <input.length()) {
if(input[i] == input[j]) {
input.erase(i);
input.erase(j);
}
else if (input[i] != input[j]) {
++i; ++j;
}
if(input[i] == '\0') {
cout<<"empty";
}
}
}
return 0;
}
int main () {
cout<<deduplicate("aabg")<<endl;
cout<<deduplicate("ag")<<endl;
cout<<deduplicate("btaabb")<<endl;
return 0;
}
When I run the code it gives me:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
There are couple of issues with your snippet,
deduplicate function is returning zero(0) all the time
j is initialized in global scope and is never reset for new string
As you erase std::string::length() is calculated on new string hence your index i and j are won't point to same laocation.
Here is the snippet with rectified error,
string deduplicate(string input) {
int i = 0;
int j = 0;
while (i < input.length()) {
j = i + 1;
bool isRepeated = false;
while (j < input.length()) {
if (input[i] == input[j]) {
input.erase(j,1);
--j; //as string length is reduced by 1
isRepeated = true;
}
++j;
}
if (isRepeated) {
input.erase(i,1); //remove first letter as well
--i;//sting length is reduced by one
}
++i;
}
return input;
}
int main() {
std::cout << deduplicate("aabg") << endl;
std::cout << deduplicate("ag") << endl;
std::cout << deduplicate("btaabb") << endl;
return 0;
}
output:
bg
ag
t
which can be even simplified as,
std::string deduplicate(std::string input) {
std::string s ="";
for (auto c : input) //loop through all char
{
int f = 0;
for (auto c1 : input)
{
if (c1 == c)
{
f++; //increment if char is found
}
}
if (f == 1)//append char only if it present ones
s += c;
}
return s;
}
You are decreasing the size of the string whenever you call string.erase() that's why the variable i eventually exceeds the "current" string size input.length() in the while loop, and you get an error std::out_of_range: basic_string when you try to access input[i] in the if and else if conditions of the loop.
Try to manually go through the loop on the string on which you got the error and you will see that i has gone out of bound (i.e. i >= input.length()) in the while loop
With C++11 and on, instead of iterating over each character and making the comparison manually, you can use std::basic_string::find_first_not_of to look forward from a position in the string and find the first character not of the current character. If the position returned by .find_first_not_of is more than 1 from the current position, you can use .erase to erase that number characters. If the return is 1, then just increment your current position and repeat.
To operate on all duplicates characters in the modified string, you simply wrap it all in an outer-loop, keep a copy of the string before before entering the inner-loop to remove duplicate characters, and compare if the modified string is equal to your copy or the .length() is zero for your exit condition.
You can do something similar to the following:
#include <iostream>
#include <string>
int main (void) {
std::string s;
while (getline (std::cin, s)) {
std::string current;
do {
size_t pos = 0;
current = s;
while (pos < s.length()) {
size_t duplicates = s.find_first_not_of (s.at(pos), pos);
if (duplicates != std::string::npos && duplicates > pos + 1)
s.erase(s.begin() + pos, s.begin() + duplicates);
else if (duplicates == std::string::npos &&
(s.end() - s.begin() - pos) > 1)
s.erase(s.begin() + pos, s.end());
else
pos++;
}
} while (current != s && s.length());
std::cout << "'" << s << "'\n";
}
}
Example Use/Output
$ echo "kcck" | ./bin/ddcpp
''
$ echo "aabggtcc" | ./bin/ddcpp
'bt'
$ echo "aabg" | ./bin/ddcpp
'bg'
$ echo "ag" | ./bin/ddcpp
'ag'
$ echo "btaabb" | ./bin/ddcpp
'bt'
There are a number of ways to approach the problem and as long as they are reasonably efficient there isn't any one right/wrong way. If you have a modern C++ compiler, letting some of the built-in container functions handle the work is generally a bit more robust than reinventing it on your own. Look things over and let me know if you have questions.

Prevent loop from echoing if another same-value array element has been already echoed in C++

First of all, sorry for the mis-worded title. I couldn't imagine a better way to put it.
The problem I'm facing is as follows: In a part of my program, the program counts occurences of different a-zA-Z letters and then tells how many of each letters can be found in an array. The problem, however, is this:
If I have an array that consists of A;A;F;A;D or anything similar, the output will be this:
A - 3
A - 3
F - 1
A - 3
D - 1
But I am required to make it like this:
A - 3
F - 1
D - 1
I could solve the problem easily, however I can't use an additional array to check what values have been already echoed. I know why it happens, but I don't know a way to solve it without using an additional array.
This is the code snippet (the array simply consists of characters, not worthy of adding it to the snippet):
n is the size of array the user is asked to choose at the start of the program (not included in the snippet).
initburts is the current array member ID that is being compared against all other values.
burts is the counter that is being reset after the loop is done checking a letter and moves onto the next one.
do {
for (i = 0; i < n; i++) {
if (array[initburts] == array[i]) {
burts++;
}
}
cout << "\n\n" << array[initburts] << " - " << burts;
initburts++;
burts = 0;
if (initburts == n) {
isDone = true;
}
}
while (isDone == false);
Do your counting first, then loop over your counts printing the results.
std::map<decltype(array[0]), std::size_t> counts;
std::for_each(std::begin(array), std::end(array), [&counts](auto& item){ ++counts[item]; });
std::for_each(std::begin(counts), std::end(counts), [](auto& pair) { std::cout << "\n\n" << pair.first << " - " pair.second; });
for (i = 0; i < n; i++)
{
// first check if we printed this character already;
// this is the case if the same character occurred
// before the current one:
bool isNew = true;
for (j = 0; j < i; j++)
{
// you find out yourself, do you?
// do not forget to break the loop
// in case of having detected an equal value!
}
if(isNew)
{
// well, now we can count...
unsigned int count = 1;
for(int j = i + 1; j < n; ++j)
count += array[j] == array[i];
// appropriate output...
}
}
That would do the trick and retains the array as is, however is an O(n²) algorithm. More efficient (O(n*log(n))) is sorting the array in advance, then you can just iterate over the array once. Of course, original array sequence gets lost then:
std::sort(array, array + arrayLength);
auto start = array;
for(auto current = array + 1; current != array + arrayLength; ++current)
{
if(*current != *start)
{
auto char = *start;
auto count = current - start;
// output char and count appropriately
}
}
// now we yet lack the final character:
auto char = *start;
auto count = array + arrayLength - start;
// output char and count appropriately
Pointer arithmetic... Quite likely that your teacher gets suspicious if you just copy this code, but it should give you the necessary hints to make up your own variant (use indices instead of pointers...).
I would do it this way.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string s;
vector<int> capCount(26, 0), smallCount(26, 0);
cout << "Enter the string\n";
cin >> s;
for(int i = 0; i < s.length(); ++i)
{
char c = s.at(i);
if(c >= 'A' && c <= 'Z')
++capCount[(int)c - 65];
if(c >= 'a' && c <= 'z')
++smallCount[(int)c - 97];
}
for(int i = 0; i < 26; ++i)
{
if(capCount[i] > 0)
cout << (char) (i + 65) << ": " << capCount[i] << endl;
if(smallCount[i] > 0)
cout << (char) (i + 97) << ": " << smallCount[i] << endl;
}
}
Note: I have differentiated lower and upper case characters.
Here's is the sample output:
output

Vector subscript out of range - vector of structures

I have the following code. It's supposed to count the number of repetitions of the given letter in a given file. However, when i try to run this i get the Vector subscript out of range. Other people with the same error were trying to access undefined parts of it, but this doesn't seem to be an issue here i think.
struct letters
{
char letter;
int repetitions=0;
};
void howManyTimes(const string &output)
{
ifstream file(output);
vector <letters> alphabet;
for (int i = 0; i < 'z' - 'a' + 1; i++)
{
alphabet[i].letter = 'a' + i;
}
string line;
while (file.eof() == 0)
{
getline(file, line);
for (char c : line)
{
if(c >= 'a' && c <= 'z')
alphabet[c - 'a'].repetitions++;
else if (c >= 'A' && c >= 'Z')
alphabet[c - 'A'].repetitions++;
}
}
cout << alphabet[10].repetitions;
}
vector <letters> alphabet; // (1)
for (int i = 0; i < 'z' - 'a' + 1; i++)
{
alphabet[i].letter = 'a' + i; // (2)
}
(1) creates an empty vector.
Inside the for loop in (2) you try to access items using the index i in an empty vector, so clearly your index is out of range.
You first have to populate the vector with some data, then you can access this data.
If you want to add new items to the vector, you can use vector::push_back (which is probably what you meant in (2)).
I don't see the part of your code where alphabet is expanded to accommodate the objects you plan to store in it. std::vector only resizes itself when you use the push_back, insert, emplace or other similar methods; it doesn't do so when accessing directly using the operator[] method.
At any rate, for a task like this, I'm not sure you'd want to use a vector, when a std::map<char, int64_t> would probably be a lot cleaner, and would let you preserve the syntax you're trying to use without tons of extra maintenance.
void howManyTimes(const string &output)
{
ifstream file(output);
map<char, int64_t> alphabet;
string line;
while (getline(file, line))
{
for (char c : line)
{
if(c >= 'a' && c <= 'z')
alphabet[c - 'a']++;
else if (c >= 'A' && c >= 'Z')
alphabet[c - 'A']++;
}
}
cout << alphabet[10];
}
Other people with the same error were trying to access undefined parts of it, but this doesn't seem to be an issue here I think.
It is definitely an issue here:
vector <letters> alphabet; // Now size of the vector is 0.
for (int i = 0; i < 'z' - 'a' + 1; i++)
{
// You are trying to access elements 0, 1, 2, ..., which do not exist.
alphabet[i].letter = 'a' + i;
}
The simplest solution is to construct your vector with appropriate size:
vector <letters> alphabet('z' - 'a' + 1);
This program is really powerful for what the question is asking (makes me wonder how my students felt when I gave it as a homework assignment :) ). Do you have to use a struct? Let's assume not, and also assume we know the size of the alphabet, and that 'a' is the first letter and 'z' is the last letter:
vector<int> repetitions(26, 0);
char nextCharacter;
while( !file.eof() )
{
cin >> nextCharacter;
nextCharacter = tolower(nextCharacter);
if(nextCharacter >= 'a' && nextCharacter <= 'z')
{
repetitions[nextCharacter - 'a']++;
}
}
To check for a letter:
cin >> letterToQuery;
cout <<"The amount of occurrences of " << letterToQuery <<" is ";
cout << repetitions[tolower(letterToQuery) - 'a'] << endl;
If you don't know the size of your alphabet, the code changes to:
vector<int> repetitions('last_alphabet' - 'a' + 1, 0);
...
if(nextCharacter >= 'a' && nextCharacter <= 'last_letter')
And finally, if you DO have to use that struct, your code changes to:
struct Letter
{
char letter;
int repetitions=0;
};
vector<Letter> alphabet;
letter temp;
for(int i = 0; i < 'last_alphabet' - 'a' + 1; ++i)
{
temp.letter = 'a' + i;
alphabet.push_back(temp);
}
// then everything else is a similar structure
char nextCharacter;
while( !file.eof() )
{
cin >> nextCharacter;
nextCharacter = tolower(nextCharacter);
if(nextCharacter >= 'a' && nextCharacter <= 'last_alphabet')
{
alphabet[nextCharacter - 'a'].repetitions++;
}
}
To check for a letter:
cin >> letterToQuery;
cout <<"The amount of occurrences of " << letterToQuery <<" is ";
cout << alphabet[tolower(letterToQuery) - 'a'].repetitions << endl;
Notice, if you replace 'last_alphabet' with 'z', you get the current alphabet.

Loop results in order

I need your help with this problem. What I want to know is how to output of a loop based on the input.
Let's say we have a program that should measure if a triangle is right or not based on the inputs of the user. The input could be something like this:
6 8 10
25 52 60
5 12 13
Using the Pythagoras formula, we can determine if a triangle is or not right
C^2=a^2+b^2
Now, with the numbers provided, the output should be:
right
wrong
right
My question is..how can I do the calculation and check if it's right or not but format the output with the same order as the input?
This is what I've tried :
#include <iostream>
#include <cmath>
using namespace std;
int rightt;
int wrong;
int main()
{
double a = 0, b = 0, c = 0;
double formula = 0;
for (int i = 0; i < 1;)
{
cin >> a >> b >> c;
formula = pow(a, 2) + pow(b, 2);
if (formula == pow(c, 2) && formula != 0)
{
//cout << "Right";
rightt = rightt + 1;
}
if (formula != pow(c, 2) && formula != 0)
{
//cout << "Wrong";
wrong = wrong + 1;
}
if (a == 0 && b == 0 && c == 0)
{
i = 1;
cout << "\n";
while (rightt > 0)
{
cout << "\n" << "Right";
rightt = rightt - 1;
}
while (wrong > 0)
{
cout << "\n" << "Wrong";
wrong = wrong - 1;
}
}
}
system("pause");
}
But my output is not as I desired. The output is first set the right, and then the wrong ones. Thanks, and I hope you understand my problem.
EDIT:
I need to have the output after the 0 0 0 is reached and not before. So If I left the commented sections , the output will be Number-output-Number-output , and what I need is to allow users to enter all numbers and tell the software that he finishes when he enters 0 0 0 , and after that give the output based on the order.
Let's imagine this input :
6 8 10 >> this is right
25 52 60 >> This is wrong
5 12 13 >> This is right
0 0 0 >> This is the values used to end the inputs
Output should be
right
wrong
right
I think that rather than counting the number of right answers and wrong answers, you can STORE all of your answers IN ORDER, in an vector. Once you are done storing all your answers, you can just loop through the answers, and print them out one by one.
If you have not learned about vectors yet, the concept is simple... you have an array like collection of data. "push_back" always tacks the data to the end of the collection of data. So if your first answer was wrong, then right, then right, first you would push_back(wrong)...resulting in a collection of [wrong]. Then you would push_back(right) resulting in a collection of [wrong, right]. Again you would push_back(right) so your final vector would be a collection in the order of [wrong, right, right]
Now you just need to loop through your collection to print out the data. The "iter" is a pointer to each spot in your list. To get the "contents of each spot" you dereference, by saying (*iter) which will provide the string result values.
#include <iostream>
#include <cmath>
#include <vector>
#include <string>
using namespace std;
int main()
{
double a = 0, b = 0, c = 0;
double formula = 0;
int numberOfResults = 0;
int currentIndex = 0;
vector<string> answers;
for (int i = 0; i < 1;)
{
cout << "Enter the number of attempts: " << "\n";
cin >> numberOfResults;
string results[numberOfResults];
cout << "Enter a b and c" << "\n";
cin >> a >> b >> c;
formula = pow(a, 2) + pow(b, 2);
if (formula == pow(c, 2) && formula != 0)
{
results[currentIndex] = "Right";
answers.push_back("Right");
}
if (formula != pow(c, 2) && formula != 0)
{
results[currentIndex] = "Wrong";
answers.push_back("Wrong");
}
if (a == 0 && b == 0 && c == 0 || currentIndex == numberOfResults-1)
{
for (int j = 0; j < numberOfResults; j++){
cout << "\n" << results[j];
}
for(auto iter = answers.begin(); iter != answers.end(); ++iter){
cout << "\n" << (*iter);
}
return 0;
}
}
system("pause");
}