-1

My code counts how many occurrences of a char appears in a string, it works but it doesnt display it how I want it probably because of the ASCII table ordering.

My Desired Output:

enter string value: Prog
P - 1
r - 1
o - 1
g - 1

But instead I get it alphabetically:

enter string value: Prog
g - 1  
o - 1      
P - 1
r - 1

Here is my code:

    char str[100] = "";
    int sum = 0;
    
    cout<<"Enter String Value: ";
    gets(str);
    
    //char counter uppercase
    for (int i = 65; i<=90; i++)
    {
      sum = 0;
      for (int j=0; j<sizeof(str); j++)
      {
        if((int)str[j] == i)
        {
          sum = sum + 1;    
        }
      }
      if (sum > 0)
      {
        cout<<(char)i<<" - "<<sum<<endl;;
      }
    }
    
    //char counter lowercase
    for (int i = 97; i<=122; i++)
    {
      sum = 0;
      for (int j=0; j<sizeof(str); j++)
      {
        if((int)str[j] == i)
        {
          sum = sum + 1;    
        }
      }
      if (sum > 0)
      {
        cout<<(char)i<<" - "<<sum<<endl;;
      }
    }

PS: I do need it to count the lowercase and uppercase letters seperately.

  • [Why gets() is so dangerous it should never be used!](https://stackoverflow.com/q/1694036/3422102) That "never" is a "NEVER EVER". `gets()` is so prone to exploit by buffer overrun it has been removed from the C library beginning with C11. – David C. Rankin Oct 11 '21 at 04:49
  • Don't use *MagicNumbers* in your code (e.g. `65`, `90`, `97`, `122`), instead use character-literals so your code is readable (e.g. `'A'`, `'Z'`, `'a'`, `'z'`) – David C. Rankin Oct 11 '21 at 04:58
  • @xdasryrytry Check out Version 3 of my answer, it solves your problem. It prints the characters in the order in which the user entered and also prints each character only once as you want. – Jason Oct 13 '21 at 06:40

3 Answers3

2

First you can make your program a lot smaller(in size) and better(general) using std::map

Version 1: Does not display the output in insertion order

#include <iostream>
#include <map>

int main() {
    std::string inputString;
    std::cout<<"Enter a string: ";
    std::getline(std::cin,inputString);
    //this map maps the char to their respective count
    std::map<char, int> charCount;
    
    for(char &c: inputString)
    {
        charCount[c]++;
        
    }
    
    for(std::pair<char, int> pairElement: charCount)
    {
        std::cout << pairElement.first <<"-" << pairElement.second<<std::endl;
    }
    return 0;
}

The output of the above Version 1 is as follows:

Enter a string: Prog
P-1
g-1
o-1
r-1

Note in the above order the order of the characters is alphabetical. If you want the output in order then do this:

Version 2: Displays the output in insertion order as you want

#include <iostream>
#include <map>
int main() {
    std::string inputString;
    std::cout<<"Enter a string: ";
    std::getline(std::cin,inputString);
    //this map maps the char to their respective count
    std::map<char, int> charCount;
    
    for(char &c: inputString)
    {
        charCount[c]++;
    }
    
    //just go through the inputString instead of map
    for(char &c: inputString)
    {
        std::cout << c <<"-" << charCount.at(c)<<std::endl;
    }
    return 0;
}

The output of this 2nd version is as follows:

Enter a string: Prog
P-1
r-1
o-1
g-1

Also the output of the above version 2 when the input has re-occuring characters like "AnoopRana" will be repeated as shown below:

Enter a string: AnoopRana
A-1
n-2
o-2
o-2
p-1
R-1
a-2
n-2
a-2

To display each character only once you can see this program which is as follows:

Version 3: For displaying each character exactly once and in insertion order

#include <iostream>
#include <map>
int main() {
    std::string inputString;
    std::cout<<"Enter a string: ";
    std::getline(std::cin,inputString);
    //this map maps the char to their respective count
    std::map<char, int> charCount;
    
    for(char &c: inputString)
    {
        charCount[c]++;
    }
    
    std::size_t i = 0;
    //just go through the inputString instead of map
    for(char &c: inputString)
    {
        std::size_t index = inputString.find(c);
        if(index != inputString.npos && (index == i)){
         std::cout << c <<"-" << charCount.at(c)<<std::endl;
         
        }
        ++i;
    }
    return 0;
}

Note

Both of these version count small and capital letters separately which is what you want.
Jason
  • 36,170
  • 5
  • 26
  • 60
  • A definite improvement over the original. For Version 2, why not use a simple `std::vector>` to preserve order and allow a simple `.push_back()` of the pair eliminating the `std::map` altogether? – David C. Rankin Oct 11 '21 at 04:55
  • I tried your suggested approach but it has some problems. In particular, how will we decide that whether we should use `push_back` to add the pair or just increment to value of the int for that character. For example lets say the input is "AnoopProg" . Now we can add a pair for the character 'A' then for 'n' and then for the first occuring `o`. But for the second occuring 'o' we should just increment the int value instead of adding a new pair. For this we will have to use some other std::container. – Jason Oct 11 '21 at 05:35
  • @DavidC.Rankin In my Version 2 we don't need `std::vector` named `keeporderVec` because we can just go through the variable `inputString` instead of creating a separate `std::vector` and going through that. Check out my edit in Version 2. – Jason Oct 11 '21 at 05:51
  • Yes i am aware of the problem you are referring. I saw it myself when i tried the program for different input before posting my answer the first time. But the problem is with the formatting(display) of the program so i don't think it is critical. The user can solve it as he/she see fits. One such solution is as follows: https://onlinegdb.com/ZGU5bv65b – Jason Oct 11 '21 at 06:11
  • What if I were to only want a repeating char to only appear once. (for example i input progo, it will only output p-1/r-1/o-2/g-1 instead of outputting o-2 twice – xdasryrytry Oct 11 '21 at 06:14
  • @xdasryrytry Check out my Edit, Version 3 of the program which does exactly that. – Jason Oct 11 '21 at 06:16
  • @xdasryrytry For displaying each character only once you can check out the program at https://onlinegdb.com/OWCpTfRHS. The same program is also given as Version 3 in my answer. – Jason Oct 11 '21 at 06:19
  • Version 3 is very nice! – David C. Rankin Oct 11 '21 at 06:19
  • @DavidC.Rankin Thanks – Jason Oct 11 '21 at 06:20
  • @AnoopRana hey ur code is amazing, wondering if its easy enough for you to add a restriction where the maximum num of characters for the string input to be 100? – xdasryrytry Oct 15 '21 at 03:20
  • @xdasryrytry You can/should ask a separate question for your last requirement(where the max number of characters is 100). Just for a hint, you can use `std::string::substr` to shorten your input string to your required number of characters. For example, lets say `std::string input = "this is a long string with many words inside it"; ` then if you write `std::string only_20char = input.substr(0, 20);` then `only_20char` will contain 20 characters from the beginning. Also, can you mark my answer as correct if it helped you. – Jason Oct 15 '21 at 04:25
1
#include <filesystem>
#include <iostream>
#include <fstream>
#include <map>
#include <cmath>
#include <chrono>
#include <algorithm>
#include <vector>
#include <execution>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <string>
#include <atomic>

std::vector<std::pair<char,int>> tfinal;

int main(int argc, char *argv[])
{

    std::string c = "rgjukuilo";
    std::for_each(std::execution::par, c.begin(), c.end(), [&](auto a){ int wr = std::count(c.begin(), c.end(), a);
        tfinal.insert(tfinal.end(), { a , wr } ); });

    for (auto c : tfinal)
        std::cout << c.first << " " << c.second << std::endl;

}
0

I like the std::map approach, but if the intent is to preserve the input order while providing a count of characters, you must have some way to check for characters that have already been seen. std::map does that for you, but does not preserve the input order. If you iterate over the original string to provide in-order output, you still need a way to avoid outputting multiple counts for the same character if it occurs more than once in the input string.

In the case where you iterate over the input string, you can keep track of the position and use std::basic_string::find to check the remainder of the string before outputting the next character.

Another way is to use a std::vector<std::pair<char, int>>. However, this does not have the convenience of std::map in that you will need to write the comparison to check whether a pair containing the character already exists or a pair needs to be added to track occurrences of that character. So you end up with an additional 10 or so lines of code.

For example, you could do:

#include <iostream>
#include <string>
#include <vector>

/* returns index if c is contained in vector, vector.size() if not */
size_t vpairContains (const std::vector<std::pair<char,int>>& vp, char c)
{
    size_t ndx = 0;                 /* zero index */
    
    for (const auto& p : vp) {      /* loop over pairs */
        if (p.first == c) {         /* compare char */
            break;
        }
        ndx++;                      /* increment index */
    }
    
    return ndx;
}

int main (void) {
    
    std::string input {};
    std::vector<std::pair<char,int>> vp {};     /* vector pairs */
    
    std::cout << "enter letters: ";             /* get/validate string */
    if (!getline (std::cin, input)) {
        return 1;
    }
    
    for (const auto& c : input) {               /* loop over chars */
        size_t ndx = vpairContains (vp, c);     /* get index in vector */
        if (ndx < vp.size()) {                  /* check if found */
            vp[ndx].second++;                   /* increment count */
        }
        else {
            vp.push_back({c, 1});               /* add new element */
        }
    }
    
    for (const auto& p : vp) {      /* output results */
        std::cout << p.first << " - " << p.second << '\n';
    }
}

Example Use/Output

Providing an input string with multiple occurrences of the same character, you could do:

$  ./bin/vector_pair_char-int
enter letters: abcd-dbbccc-*-
a - 1
b - 3
c - 4
d - 2
- - 3
* - 1

Look things over and let me know if this is what you were attempting to accomplish. There are several ways you can do this. The std::map approach is also good, probably even preferred, but @AnooopRana already has a good example to work from there.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85