equal_range question

I'm using a multimap to store a collection of PLAY objects.
The multimap entries consist of a score (int) and a copy of the PLAY object.

I'm stuck in two places.
First, There should be an easy way to determine how many entries are in the equal range,

Second, after calculating a random index, how to refer to the n'th entry in the range.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MMAP_PLAYS : public std::multimap <int, PLAY>
{
public:
	typedef pair<const_iterator, const_iterator> mmap_range_t;

	MMAP_PLAYS();
	virtual ~MMAP_PLAYS() = default;

	PLAY * get_random_top_play() const; 
	void print_top_n(int n) const;

	void Insert(const PLAY& play);
	
};


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
PLAY * MMAP_PLAYS::get_random_top_play() const
{
	const_reverse_iterator	crit;
	PLAY *			last_play;
	int				high_score = 0;
	pair<const_iterator, const_iterator>	range;
	int				num_plays = 0;
	int				n;
	pair<int,PLAY>	last_pr;
	pair<int, PLAY>	rand_pr;
	PLAY *			rand_play;
	const_iterator	rand_iter; 

	crit = crbegin();
	assert(crit != crend());				//	MMAP should not be empty
	last_pr = *crit;						//	Last entry/entries have highest score
	last_play = &last_pr.second;
	high_score = last_play->get_points();
	range = equal_range(high_score);		//	Get range of plays with high score
	//	*** Count the number of iterators in the range
	//	*** There should be a better way to do this
	for (auto i = range.first; i != range.second; i++)
		num_plays++;							
	uniform_int_distribution<int> distribution(0, num_plays - 1);
	n = distribution(g_random_generator);
	//	*** Here's my problem
	//	*** How to advance to a random iterator
	//	I could count as I did above, but there must be a better way
	rand_iter = range.first + n;	// *** Intellisense hilights the + 
	rand_pr = *rand_iter;
	rand_play = &rand_pr.second;
	return rand_play;
}



Severity	Code	Description	Project	File	Line	Suppression State
Error (active)	E0349	no operator "+" matches these operands	

Severity	Code	Description	Project	File	Line	Suppression State
Error	C2676	binary '+': '_Ty1' does not define this operator or a conversion to a type acceptable to the predefined operator		

If you just want to know the number of elements with a certain key then you could use the count member function.

 
num_plays = count(high_score);

Otherwise you could get the number of elements in the range by using std::distance.

 
num_plays = std::distance(range.first, range.second);


To advance a non-random access iterator a certain number of steps you can use either std::advance:

1
2
rand_iter = range.first;
std::advance(rand_iter, n);

Or std::next:

 
rand_iter = std::next(range.first, n);
Last edited on
Oh, and you are returning a dangling pointer.

1
2
3
4
pair<int, PLAY> rand_pr;
...
rand_play = &rand_pr.second;
return rand_play;

You return a pointer to rand_pr.second but rand_pr (including rand_pr.second) is destroyed when the function ends.
Last edited on
Consider something like this - where the map is sorted descending rather than ascending:

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
#include <map>
#include <iostream>
#include <string>
#include <functional>
#include <iterator>
#include <random>

std::mt19937 rng(std::random_device {}());

struct PLAY {
	std::string name;
};

using MYMAP = std::multimap<int, PLAY, std::greater<int>>;

auto top(const MYMAP& mp) {
	const auto high_score { mp.equal_range(mp.begin()->first) };
	std::uniform_int_distribution<ptrdiff_t> song(ptrdiff_t(0), std::distance(high_score.first, high_score.second) - 1);
	auto chose { mp.begin() };

	std::advance(chose, song(rng));
	return chose;
}

int main() {
	const MYMAP myMap { {4, {"qwet"}}, {3, {"asdf"}}, {4, {"poiu"}}, {2, {"lkj"}}, {4, {"mnbv"}} };

	std::cout << "Chosen top song is " << top(myMap)->second.name << '\n';
}

Last edited on
Topic archived. No new replies allowed.