Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting occurrences in a vector

Tags:

c++

This program reads strings of numbers from a txt file, converts them to integers, stores them in a vector, and then tries to output them in an organized fashion like so....

If txt file says:

7 5 5 7 3 117 5

The program outputs:

3
5   3
7   2
117

so if the number occurs more than once it outputs how many times that happens. Here is the code so far.

#include "std_lib_facilities.h"

int str_to_int(string& s)
{
    stringstream ss(s);
    int num;
    ss >> num;
    return num;
}

int main()
{
    cout << "Enter file name.\n";
    string file;
    cin >> file;
    ifstream f(file.c_str(), ios::in);

    string num;
    vector<int> numbers;
    while(f>>num)
    {
        int number = str_to_int(num);
        numbers.push_back(number);
    }

    sort(numbers.begin(), numbers.end());

    for(int i = 0; i < numbers.size(); ++i)
    {
     if(i = 0 && numbers[i]!= numbers[i+1]) cout << numbers[i] << endl;
     if(i!=0 && numbers[i]!= numbers[i-1])
     {
          cout << numbers[i] << '\t' << counter << endl;
          counter = 0;
     }
     else ++counter;
    }
 }

Edit: Program is getting stuck. Looking for an infinite loop right now.

like image 663
trikker Avatar asked Jul 30 '09 03:07

trikker


4 Answers

You could use a map of numbers to counters:

typedef map<int,unsigned int> CounterMap;
CounterMap counts;
for (int i = 0; i < numbers.size(); ++i)
{
   CounterMap::iterator it(counts.find(numbers[i]));
   if (it != counts.end()){
      it->second++;
   } else {
      counts[numbers[i]] = 1;
   }
}

... then iterate over the map to print results.

EDIT: As suggested by lazypython: if you have the TR1 extensions [wikipedia.org] available, unordered_map should have better performance...

typedef std::tr1::unordered_map<int,unsigned int> CounterMap;
CounterMap counts;
for (int i = 0; i < numbers.size(); ++i)
{
   CounterMap::iterator it(counts.find(numbers[i]));
   if (it != counts.end()){
      it->second++;
   } else {
      counts[numbers[i]] = 1;
   }
}
like image 143
Adam Holmberg Avatar answered Sep 22 '22 02:09

Adam Holmberg


How about using a map, where the key is the number you're tracking and the value is the number of occurrences?

If you must use a vector, you've already got it sorted. So just keep track of the number you previously saw. If it is the same as the current number, increment the counter. Every time the number changes: print out the current number and the count, reset the count, set the last_seen number to the new number.

like image 26
bstpierre Avatar answered Sep 18 '22 02:09

bstpierre


Using a map is the practical solution. What you should do is to solve this problem :)

This is called frequency counter. So, you have a sorted vector and all what you have to do is to count successive equal numbers. In other words, you have to check each number with its successor.

for(size_t i = 0; i < numbers.size(); i++)
{
    size_t count = 1;

    size_t limit = numbers.size() - 1;
    while(i < limit  && numbers[i] == numbers[i+1])
    {
        count++;
        i++;
    }

    std::cout << numbers[i] << "\t" << count << std::endl;
}   
like image 43
Khaled Alshaya Avatar answered Sep 20 '22 02:09

Khaled Alshaya


This program reads strings of numbers from a txt file, converts them to integers, stores them in a vector, and then tries to output them in an organized fashion like so....(emphasis added)

What is the point of this storage step? If you are reading the numbers from a file, then you already have them in order, ready to be processed (counted) one at time, as you encounter them.

However, I would need a way for it to know when it sees a new number.

I advise you to have a look at std::set or std::map. I expect either of these containers would do what you're looking for.

like image 22
SingleNegationElimination Avatar answered Sep 18 '22 02:09

SingleNegationElimination