PPP2 Chapter 11 Exercise 16

I need to know how to check how many times numbers were entered and print the numbers out from least to greatest while printing how many times a number that was repeated was entered (printed as a number written next to the number it's talking about).

Here's my code so far (specs for the exercise are written in it in comments, along with an example output (spaces probably not preserved properly):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// chapter11ex16.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 16
// Exercise Specification:
/**
 * Write a program to read a file of whitespace-separated numbers and 
 * output them in order (lowest value first), one value per line. Write a value
 * only once, and if it occurs more than once write the count of its 
 * occurrences on its line. For example, 7 5 5 7 3 117 5 should give
 *		3
 *		5   3
 *		7   2
 *		117
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <algorithm>
#include <vector>

int main()
{
	using namespace std;
	cout << "Please enter some numbers separated by spaces:\n";
	vector<int> numbers;
	for (int number; cin >> number;)
	{
		cin.ignore();
		numbers.push_back(number);
		sort(numbers.begin(), numbers.end());
		if (numbers[number - 1] == numbers[number])
		{

		}
	}
	
	
}
This can easily done with a map:

http://www.cplusplus.com/reference/map/map/?kw=map
@coder777,
maps will be covered in chapter 21.

I would create a struct to store the number and count and store it in a vector.
Whenever you read a number check if it is in the vector, if yes increment the count, otherwise create a new struct and add it to the vector. At the end you print the vector.

1
2
3
4
5
6
7
8
9
10
struct NumberInfo
{
  int number,
      count;

  bool operator == (const NumberInfo& ni) // for use of find
  {
    return this->number == ni.number;
  }
};
Write a program to read a file of whitespace-separated numbers

Where is this part of the assignment?

1
2
3
4
5
6
	for (int number; cin >> number;)
	{
		cin.ignore();
		numbers.push_back(number);
		sort(numbers.begin(), numbers.end());
		if (numbers[number - 1] == numbers[number])

You're invoking Undefined Behavior in this snippet, can you tell us where and why (Note: there is at least two places in this small snippet that are problems?

Why are you sorting the numbers every time you go through the loop? Sorting even a small vector multiple times can take a lot of resources, imagine what would happen if the vector grows to several hundred or several thousand elements.

What's with the cin.ignore()? By default the extraction operator skips leading whitespace characters so it shouldn't be needed.

@Thomas: My compiler is complaining about the assignment operator not being defined for NumberInfo (if I understand correctly). Quoting the message would make the post too long, so I can't do that. So I'll show my code and show the quote in another post if needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// chapter11ex16.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 16
// Exercise Specification:
/**
 * Write a program to read a file of whitespace-separated numbers and 
 * output them in order (lowest value first), one value per line. Write a value
 * only once, and if it occurs more than once write the count of its 
 * occurrences on its line. For example, 7 5 5 7 3 117 5 should give
 *		3
 *		5   3
 *		7   2
 *		117
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <algorithm>
#include <vector>

struct NumberInfo
{
	int m_number;
	int m_count;
	bool operator==(const NumberInfo &ni);
	NumberInfo operator=(const NumberInfo &ni);
	NumberInfo(const int number, const int count);
};

void print_numbers_count(const std::vector<NumberInfo> &ni_v);

int main()
{
	using namespace std;
	cout << "Please enter some numbers separated by spaces:\n";
	vector<NumberInfo> ni_v;
	int number;
	size_t iter = 0;
	while (cin >> number)
	{
		cin.ignore();
		ni_v.push_back(NumberInfo{ number, 0 });
		if (ni_v[number].m_number == number)
		{
			++ni_v[iter].m_count;
		}
		++iter;
	}
}

NumberInfo::NumberInfo(const int number, const int count)
	:m_number{ number }, m_count{ count }
{
}

bool NumberInfo::operator==(const NumberInfo &ni)
{
	return this->m_number == ni.m_number;
}

NumberInfo NumberInfo::operator=(const NumberInfo &ni)
{
	this->m_number = ni.m_number;
	this->m_count = ni.m_count;
	return *this;
}

void print_numbers_count(const std::vector<NumberInfo> &ni_v)
{
	using namespace std;
	sort(ni_v.begin(), ni_v.end());
	for (const auto &x : ni_v)
	{
		cout << x.m_number;
		if (x.m_count > 0)
		{
			cout << '\t' << x.m_count;
		}
		cout << '\n';
	}
}
My compiler is complaining about the assignment operator not being defined for NumberInfo (if I understand correctly).

I don't think you're understanding correctly (although I didn't bother tossing your code in a compiler.) Your operator= requires the presence of a copy constructor (as it returns by value,) but you don't have one. Typically, the return type of an operator= should be a reference.

As well, std::sort uses operator< by default for ordering and no such operator is provided for NumberInfo.
if you can't use std::map, you could use std::vector<std::pair<int,int>> as a psuedo-map on the following lines:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <fstream>
#include <vector>
#include <tuple>
#include <algorithm>
#include <utility>

int main()
{
    std::ifstream inFile{"F:\\test.txt"};
    std::vector <std::pair<int, int>> fileNumbers{};
    if(inFile)
    {
        int temp{};
        while (inFile >> temp )
        {
            auto itr = std::find_if(fileNumbers.begin(), fileNumbers.end(),
                [&temp](const std::pair<int, int>& element){return element.first == temp;});
            if (inFile && itr != fileNumbers.end())//if number already exists increment value ...
            {
                ++ (*itr).second;
            }
            else
            if(inFile)
            {
                    fileNumbers.push_back(std::make_pair(temp, 1));//else make pair with initial value of 1
            }
        }
    }
    std::sort(fileNumbers.begin(), fileNumbers.end());
    for (const auto& elem : fileNumbers)
    {
        std::cout << elem.first << " " << elem.second << "\n";
    }
}
/*tested with file:
9 14 3 9 19 7 6 5 8 1 13 9 2 36 1 9 4 32 8 198 */


My compiler is complaining about the assignment operator not being defined for NumberInfo (if I understand correctly).

You don't need an asignment operator nor a copy constructor since you don't use dynamic memory.

Here is a simple idea, maybe it will work for you:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Write a program to read a file of whitespace - separated numbers and output them in order(lowest value first), one value
// per line.Write a value only once, and if it occurs more than once write the count of its occurrences on its line.For
// example, 7 5 5 7 3 117 5 should give3
// 5     3
// 7     2
// 117

#include "std_lib_facilities.h"

struct NumberInfo
{
  int number,
      count;

  NumberInfo(int number, int count)
  {
    this->number = number;
    this->count = count;
  }
};

// searches for a number num in the vector numbers
// return true if the number is found 
// and sets index to the index of the elem that contains num
// if not found return false and leaves index untouched

bool FindNumber(vector<NumberInfo>& numbers, int num, size_t& index)
{
  for (size_t i = 0; i < numbers.size(); i++)
  {
    if (numbers[i].number == num)
    {
      index = i;
      return true;
    }
  }
  return false;
}

int main()
{
  vector<NumberInfo> numbers;
  size_t pos = 0;

  for (int num; cin >> num;)
  {
    if (FindNumber(numbers, num, pos))
    {
      numbers[pos].count++;
    }
    else
    {
      numbers.push_back(NumberInfo(num, 1));
    }
  }
  sort(numbers.begin(), numbers.end(),
    [](const NumberInfo& n1, const NumberInfo& n2)
    {
      // your sort code
    });

  // print numbers here
  system("pause");
  return 0;
}
My compiler keeps complaining about not being able to find the overloaded function I'm trying to call. Did I not define them properly, or am I still not overloading the functions I need?

I can't quote the messages from the compiler here because it'd make the post more than 8,192 characters long. So I'll just show you my code again. Isn't there a way I can show you guys the error messages, though?

Anyway, here's the code again:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// chapter11ex16.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 16
// Exercise Specification:
/**
 * Write a program to read a file of whitespace-separated numbers and 
 * output them in order (lowest value first), one value per line. Write a value
 * only once, and if it occurs more than once write the count of its 
 * occurrences on its line. For example, 7 5 5 7 3 117 5 should give
 *		3
 *		5   3
 *		7   2
 *		117
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <algorithm>
#include <vector>

struct NumberInfo
{
	int m_number;
	int m_count;
	bool operator==(const NumberInfo &ni);
	bool operator<(const NumberInfo &ni);
	NumberInfo &operator=(const NumberInfo &ni);
	NumberInfo(const int number, const int count);
	NumberInfo();
};

void print_numbers_count(const std::vector<NumberInfo> &ni_v);

int main()
{
	using namespace std;
	cout << "Please enter some numbers separated by spaces:\n";
	vector<NumberInfo> ni_v;
	int number;
	size_t iter = 0;
	while (cin >> number)
	{
		cin.ignore();
		ni_v.push_back(NumberInfo{ number, 0 });
		if (ni_v[iter].m_number == number)
		{
			++ni_v[iter].m_count;
		}
		++iter;
	}
}

NumberInfo::NumberInfo(const int number, const int count)
	:m_number{ number }, m_count{ count }
{
}

NumberInfo::NumberInfo()
	: m_number{}, m_count{}
{
}

bool NumberInfo::operator==(const NumberInfo &ni)
{
	return this->m_number == ni.m_number;
}

bool NumberInfo::operator<(const NumberInfo &ni)
{
	return this->m_number < ni.m_number;
}

NumberInfo &NumberInfo::operator=(const NumberInfo &ni)
{
	this->m_number = ni.m_number;
	this->m_count = ni.m_count;
	return *this;
}

void print_numbers_count(const std::vector<NumberInfo> &ni_v)
{
	using namespace std;
	sort(ni_v.begin(), ni_v.end());
	for (const auto &x : ni_v)
	{
		cout << x.m_number;
		if (x.m_count > 0)
		{
			cout << '\t' << x.m_count;
		}
		cout << '\n';
	}
}


@Thomas: I can still use a class constructor type function as a constructor if I use your idea, right?
What compiler / IDE do you use?
1
2
3
4
void print_numbers_count(const std::vector<NumberInfo> &ni_v)
{
	using namespace std;
	sort(ni_v.begin(), ni_v.end());


How can you sort a const vector?

You really need to study up on const correctness. You have several places where you have failed to properly const qualify your class functions and places, like above, where you've passed a const then tried to modify that const.

1
2
//	bool operator<(const NumberInfo &ni); // Should be:
	bool operator<(const NumberInfo &ni) const;


https://isocpp.org/wiki/faq/const-correctness


Also posting the error messages would be helpful, and in this case the first dozen or so would have been helpful.

1
2
3
4
5
6
7
8
9
10
||=== Build: Debug (compiler: gcc 6.1.0) ===|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/predefined_ops.h||In instantiation of ‘constexpr bool __gnu_cxx::__ops::_Iter_less_iter::operator()(_Iterator1, _Iterator2) const [with _Iterator1 = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >; _Iterator2 = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >]’:|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/stl_algo.h|1844|required from ‘void std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]’|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/stl_algo.h|1882|required from ‘void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]’|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/stl_algo.h|1968|required from ‘void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >; _Compare = __gnu_cxx::__ops::_Iter_less_iter]’|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/stl_algo.h|4714|required from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = __gnu_cxx::__normal_iterator<const NumberInfo*, std::vector<NumberInfo> >]’|
/main.cpp|86|required from here|
/opt/gcc/6.1.0/include/c++/6.1.0/bits/predefined_ops.h|43|error: no match foroperator<’ (operand types are ‘const NumberInfo’ and ‘const NumberInfo’)|
main.cpp|71|note: candidate: bool NumberInfo::operator<(const NumberInfo&) <near match>|
main.cpp|71|note:   passing ‘const NumberInfo*’ as ‘this’ argument discards qualifiers|


In the above the last two messages point you to the actual problem. That "near match" and "discards qualifiers" are two very very key words, and the fact that they are pointing to your code is also a key.
Last edited on
Let's take a step and recognize the tricky part with this problem.

1. To count the number of occurrences of a number, you need to lookup the count by the number.
2. To display the numbers in order of frequency, you need to sort the numbers by count.

This suggests two data structures.

For what it's worth, this is actually a pretty common problem in the real world: "what are the most common occurences of X?" At work I usually get the X's into a file, one per line, and then do sort | uniq -c | sort -n on the command line.
sort sorts the lines.
uniq -c prints unique lines, prepending the number of occurences (the count)
sort -n sorts numerically (by the count)
@OP,
here is a simple solution:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Write a program to read a file of whitespace - separated numbers and output them in order(lowest value first), one value
// per line.Write a value only once, and if it occurs more than once write the count of its occurrences on its line.For
// example, 7 5 5 7 3 117 5 should give
// 3
// 5     3
// 7     2
// 117

#include "std_lib_facilities.h"

struct NumberInfo
{
  int number,
      count;

  NumberInfo(int number, int count)
  {
    this->number = number;
    this->count = count;
  }
};

// searches for a number num in the vector numbers
// return true if the number is found 
// and sets index to the index of the elem that contains num
// if not found return false and leaves index untouched

bool FindNumber(vector<NumberInfo>& numbers, int num, size_t& index)
{
  for (size_t i = 0; i < numbers.size(); i++)
  {
    if (numbers[i].number == num)
    {
      index = i;
      return true;
    }
  }
  return false;
}

int main()
{
  vector<NumberInfo> numbers;
  size_t pos = 0;

  for (int num; cin >> num;)
  {
    if (FindNumber(numbers, num, pos))
    {
      numbers[pos].count++;
    }
    else
    {
      numbers.push_back(NumberInfo(num, 1));
    }
  }
  sort(numbers.begin(), numbers.end(),
    [](const NumberInfo& n1, const NumberInfo& n2)
    {
      return n1.number < n2.number;
    });

  for (NumberInfo& ni : numbers)
  {
    cout << ni.number;
    if (ni.count > 1)
      cout << "\t" << ni.count;

    cout << "\n";
  }
  system("pause");
  return 0;
}


OUTPUT
7 5 5 7 3 117 5
3
5       3
7       2
117

Okay, the errors were taken care of, but now there's a logic error; here's an example output:

Please enter some numbers separated by spaces:
7 5 5 7 3 117 5 |
3 1
5 1
5 1
5 1
7 1
7 1
117 1
Please enter a character to exit
Press any key to continue . . .


What's with the 1's? And why didn't wait for a character input exiting (that's two logic errors, not one, but yeah).

Here's the code (I took the sort() out of the printing function and put it in main, so that the vector is sorted before being passed to the function):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// chapter11ex16.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 7 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 16
// Exercise Specification:
/**
 * Write a program to read a file of whitespace-separated numbers and 
 * output them in order (lowest value first), one value per line. Write a value
 * only once, and if it occurs more than once write the count of its 
 * occurrences on its line. For example, 7 5 5 7 3 117 5 should give
 *		3
 *		5   3
 *		7   2
 *		117
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <algorithm>
#include <vector>

struct NumberInfo
{
	int m_number;
	int m_count;
	bool operator<(const NumberInfo &ni);
	NumberInfo(const int number, const int count);
	NumberInfo();
};

void print_numbers_count(const std::vector<NumberInfo> &ni_v);

int main()
{
	using namespace std;
	cout << "Please enter some numbers separated by spaces:\n";
	vector<NumberInfo> ni_v;
	int number;
	size_t iter = 0;
	while (cin >> number)
	{
		cin.ignore();
		ni_v.push_back(NumberInfo{ number, 0 });
		if (ni_v[iter].m_number == number)
		{
			++ni_v[iter].m_count;
		}
		++iter;
	}
	sort(ni_v.begin(), ni_v.end());
	print_numbers_count(ni_v);
	cin.clear();
	keep_window_open();
}

NumberInfo::NumberInfo(const int number, const int count)
	:m_number{ number }, m_count{ count }
{
}

NumberInfo::NumberInfo()
	: m_number{}, m_count{}
{
}

bool NumberInfo::operator<(const NumberInfo &ni)
{
	return this->m_number < ni.m_number;
}

void print_numbers_count(const std::vector<NumberInfo> &ni_v)
{
	using namespace std;
	for (const auto &x : ni_v)
	{
		cout << x.m_number;
		if (x.m_count > 0)
		{
			cout << '\t' << x.m_count;
		}
		cout << '\n';
	}
}


Edit: Okay, I got it to work.
Last edited on
Topic archived. No new replies allowed.