Sort multidimensional map based on pair value - c++

I have a map defined as follows:
std::map<std::string, std::vector<std::pair<std::string, std::string>>> groupList;
GOAL:
I have a list of groups of commands. I want to sequence through each group in order. In order to do this, each group has a "sequence" value. I want to sort the overall list based on this value from smallest to largest. Essentially, I have a list of elements, which each have an accompanying list of other values within them specific to each parent. I want to sort the list of parents by a specific pair or value within the child lists.
For me to visualize this, I created an array in PHP with a similar structure. I'm not sure exactly how to visualize a C++ map so this is just me making assumptions. 1, 2, 3 are the keys of the map.
Array
(
[1] => Array
(
[groupID] => 1
[sequence] => 0
[command] => DefaultState
)
[2] => Array
(
[groupID] => 2
[sequence] => 2
[command] => Restart
)
[3] => Array
(
[groupID] => 3
[sequence] => 1
[command] => Beep
)
)
I'd like to sort this map based on the value of a particular pair inside, in this case "sequence". Element "2" should be below element "3" when sorting using the "sequence" value. The end result would look like this:
Array
(
[1] => Array
(
[groupID] => 1
[sequence] => 0
[command] => DefaultState
)
[3] => Array
(
[groupID] => 3
[sequence] => 1
[command] => Beep
)
[2] => Array
(
[groupID] => 2
[sequence] => 2
[command] => Restart
)
)
I'm sorry to mix languages here but there's no simple way for me to dump a map that depicts its structure (that I know of).
At first, my map was set up like so:
std::map<std::string, std::map<std::string, std::string>> groupList;
This was easier for me to add elements and then access them later, but I figured a vector pair would be easier to use for sorting. I would prefer to use the latter definition for ease. I was looking at using std::sort and boost but I've had no luck implementing for this specific case.
Any comments/help is welcome. Thanks!

Whenever I see a datastructure, I imagine to have a definition for it handy:
struct Item {
int groupID;
int sequence;
std::string command;
}
Now you can trivially define the array:
Item arr[] = {
{ 1, 0, "DefaultState" },
{ 2, 2, "Restart" },
{ 3, 1, "Beep" },
};
Since it's a simple aggregate with value semantics, you can just define an order and sort it:
struct Item {
int groupID;
int sequence;
std::string command;
bool operator<(Item const& other) const {
return sequence < other.sequence;
}
};
Demo
Adding a streaming operator<< and we get a full working demo:
Live On Coliru
#include <iostream>
struct Item {
int groupID;
int sequence;
std::string command;
bool operator<(Item const& other) const {
return sequence < other.sequence;
}
friend std::ostream& operator<<(std::ostream& os, Item const& i) {
return os << "Item { group:" << i.groupID << ", sequence:" << i.sequence << ", command:'" << i.command << "' }";
}
};
#include <algorithm>
int main() {
Item arr[] = {
{ 1, 0, "DefaultState" },
{ 2, 2, "Restart" },
{ 3, 1, "Beep" },
};
std::sort(std::begin(arr), std::end(arr));
for (auto& item : arr)
std::cout << item << '\n';
}
Prints
Item { group:1, sequence:0, command:'DefaultState' }
Item { group:3, sequence:1, command:'Beep' }
Item { group:2, sequence:2, command:'Restart' }

Expanding on my earlier answer, and in case you're really looking for something more advanced, here's what comes to mind using Boost MultiIndex containers:
Live On Coliru
#include <fstream>
#include <iostream>
struct Item {
int groupID;
int sequence;
std::string command;
friend std::ostream& operator<<(std::ostream& os, Item const& i) {
return os << "Item { group:" << i.groupID << ", sequence:" << i.sequence << ", command:'" << i.command << "' }";
}
};
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
namespace bmi = boost::multi_index;
using Table = bmi::multi_index_container<
Item,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_group>,
bmi::member<Item, int, &Item::groupID>
>,
bmi::ordered_unique<
bmi::tag<struct by_sequence>,
bmi::member<Item, int, &Item::sequence>
>
>
>;
#include <algorithm>
#include <map>
int main() {
Table arr = {
{ 1, 0, "DefaultState" },
{ 2, 2, "Restart" },
{ 3, 1, "Beep" },
};
for (auto& item : arr.get<by_group>())
std::cout << item << '\n';
std::cout << "\nsorted by sequence:\n";
for (auto& item : arr.get<by_sequence>())
std::cout << item << '\n';
}
Prints
Item { group:1, sequence:0, command:'DefaultState' }
Item { group:2, sequence:2, command:'Restart' }
Item { group:3, sequence:1, command:'Beep' }
sorted by sequence:
Item { group:1, sequence:0, command:'DefaultState' }
Item { group:3, sequence:1, command:'Beep' }
Item { group:2, sequence:2, command:'Restart' }

Related

Counting Duplicates in C++ - multiset?

UPD:-
Value Instances
 2   3
 3   2
 5   1
I want to limit the count to 1 for all the instances present in the multiset.
#include<bits/stdc++.h>
using namespace std;
int main() {
multiset<int> p1;
p1.insert(5);
p1.insert(2);
p1.insert(3);
p1.insert(3);
p1.insert(2);
p1.insert(2);
for(auto itr : p1) {
if(p1.count(itr) > 1)
p1.erase(itr);
cout << itr;
}
}
How to fix this ?
My comment:
In that case, you should use a std::set<int> because that is actually what matches your requirement. You could use also a std::map<int, int> to map the key to the number of occurrences if you like.
OPs reply:
Can you add this to a full-fledged answer so that I can accept it for this question?
Here we go:
Just filtering duplicates:
#include <iostream>
#include <set>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once
using Table = std::set<int>;
Table table;
for (int value : sample) table.insert(value);
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry << "\n";
}
}
Output:
Value 2
Value 3
Value 5
Demo on coliru
Counting the number of occurrences:
#include <iostream>
#include <map>
int main()
{
int sample[] = { 5, 2, 3, 3, 2, 2 };
// add all values at most once but count the number of occurrences
using Table = std::map<int, unsigned>;
Table table;
for (int value : sample) ++table[value];
// output the result
for (const Table::value_type& entry : table) {
std::cout << "Value " << entry.first << " (" << entry.second << " times)\n";
}
}
Output:
Value 2 (3 times)
Value 3 (2 times)
Value 5 (1 times)
Demo on coliru
The trick:
The std::map::operator[] inserts an element if the key is not yet there. This element (in this case std::pair<const int, unsigned>) is default initialized which grants that it starts as { key, 0 }.
So, there are two cases:
The key is not yet there:
The element is created as { key, 0 } and the value (.second of the element) is incremented immediately which results in { key, 1 }.
The key is already there:
The value (.second of the element) is incremented again.
A variation on filtering duplicates:
This keeps the original input order but removes repetitions (by book-keeping in a separate std::set).
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator iterRead = sample.begin();
Sample::iterator iterWrite = sample.begin();
for (; iterRead != sample.end(); ++iterRead) {
if (table.insert(*iterRead).second) *iterWrite++ = *iterRead;
}
sample.erase(iterWrite, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
Output:
Value 5
Value 2
Value 3
Demo on coliru
The trick:
std::set::insert() returns a pair of iterator and bool.
The iterator points to the key in the set (inserted or already been there).
The bool denotes if the key was inserted (true) or was already there (false).
The other trick:
Just erasing every found duplicate from the std::vector would result in the worse complexity O(n²).
Hence, two iterators are used, one for reading and one for writing. Thereby, every input value which is not yet in the bookkeeping table (and hence occurs the first time) is written back, otherwise not.
So, every value which occurred the first time is shifted towards the beginning and appended to the previous values which occurred the first time each. Additionally, the iterWrite points past the last written element after the loop and can be used to erase the rest (which contains left input values which are all duplicates).
The complexity of this algorithm is O(n) – much better than the naive approach.
Btw. the standard algorithms std::remove(), std::remove_if() does it the same way.
Thus, the same algorithm could be achieved with std::remove_if():
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
using Sample = std::vector<int>;
Sample sample = { 5, 2, 3, 3, 2, 2 };
// remove duplicates
using Table = std::set<int>;
Table table;
Sample::iterator last
= std::remove_if(sample.begin(), sample.end(),
[&](int value) { return !table.insert(value).second; });
sample.erase(last, sample.end());
// output the result
for (const Sample::value_type& entry : sample) {
std::cout << "Value " << entry << "\n";
}
}
Output:
like above
Demo on coliru
#include <iostream>
#include <set>
using namespace std;
int main()
{
multiset<int> p1;
p1.insert(5);
p1.insert(2);
p1.insert(3);
p1.insert(4);
p1.insert(2);
p1.insert(2);
for (auto iter = p1.begin(); iter != p1.end();)
{
p1.count(*iter) > 1 ? iter = p1.erase(iter) : iter++;
}
for (auto & iter : p1)
{
cout << iter << ", ";
}
return 0;
}

How can I construct one map from another map?

I am trying to create a map from another map using a comparator function that the new value in the key value pair is not same as the previous value in the key value pair stored in the map.
I am getting a compilation error while compiling below code. What is the issue is with that code? Is there a better way to accomplish this as well?
#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <functional>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
// Declaring the type of Predicate that accepts 2 pairs and return a bool
typedef std::function<bool(std::pair<std::string, int>, std::pair<std::string, int>)> Comparator;
// Defining a lambda function to compare two pairs. It will compare two pairs using second field
Comparator compFunctor =
[](std::pair<std::string, int> elem1 ,std::pair<std::string, int> elem2)
{
return elem1.second != elem2.second;
};
// Declaring a set that will store the pairs using above comparision logic
std::map<std::string, int, Comparator> setOfWords(
mapOfWordCount.begin(), mapOfWordCount.end(), compFunctor);
return 0;
}
The expected output of the second map is:
{ "aaa", 10 }
{ "ddd", 41 }
{ "bbb", 62 }
This means, that { "ccc", 10 } has to be ignored.
Excerpt from the error:
sortMap.cpp:25:70: required from here
/opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/stl_tree.h:1422:8:
error: no match for call to
‘(std::function, int>,
std::pair, int>)>) (const
std::basic_string&, const key_type&)’
&& _M_impl._M_key_compare(_S_key(_M_rightmost()), __k))
^ In file included from /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/bits/stl_algo.h:66:0,
from /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/algorithm:62,
from sortMap.cpp:4: /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:2174:11:
note: candidate is:
class function<_Res(_ArgTypes...)>
^ /opt/tools/installs/gcc-4.8.3/include/c++/4.8.3/functional:2466:5:
note: _Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes
...) const [with _Res = bool; _ArgTypes =
{std::pair,
std::allocator >, int>, std::pair, std::allocator >, int>}]
function<_Res(_ArgTypes...)>::
^
This is a solution according to the intention described by OP.
Sample code:
#include <iostream>
#include <map>
#include <set>
#include <vector>
int main()
{
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = {
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
};
// auxiliary set of values
std::set<int> counts;
// creating a filtered map
std::vector<std::pair<std::string, int> > mapOfWordCountFiltered;
for (const std::map<std::string, int>::value_type &entry : mapOfWordCount) {
if (!counts.insert(entry.second).second) continue; // skip duplicate counts
mapOfWordCountFiltered.push_back(entry);
}
// output
for (const std::pair<std::string, int> &entry : mapOfWordCountFiltered) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
There is no custom predicate used as the standard predicate (std::less<Key>) is sufficient for the solution (for map as well as set).
The filtered map doesn't even use a std::map as there is no necessity for this. (The entries are already sorted, the filtering is done by an extra std::set<int>.)
Actually, I have no idea how to perform this with a custom predicate as I don't know how to keep the (required) order of map with the extra check for duplicated values.
Isn't there a way to create a comparator that makes sure that another "key, value" is not inserted, if the value is already present in the map previously corresponding to a different key? This would save extra space that I would use by creating another set.
I have thought about this a while. Yes, it is possible but I wouldn't recommend it for productive code.
std::map::insert() probably calls std::map::lower_bound() to find the insertion point (i.e. iterator). (The std::map::lower_bound() in turn will use our custom predicate.) If the returned iterator is end() the entry is inserted at end. Otherwise, the key at this iterator is compared with the one which is provided as new (to be inserted). If it is equal the insertion will be denied otherwise the new entry is inserted there.
So, to deny insertion of an entry with duplicated value, the predicate has to return false regardless of comparison of keys. For this, the predicate has to do extra checks.
To perform these extra checks, the predicate needs access to the whole map as well as to the value of entry to be inserted. To solve the first issue, the predicate gets a reference to the map where it is used in. For the second issue, I had no better idea as to use a std::set<std::pair<std::string, int> > instead of the original std::map<std::string, int>. As there is already a custom predicate involved, the sorting behavior can be adjusted sufficiently.
So, this is what I got:
#include <iostream>
#include <map>
#include <set>
#include <vector>
typedef std::pair<std::string, int> Entry;
struct CustomLess;
typedef std::set<Entry, CustomLess> Set;
struct CustomLess {
Set &set;
CustomLess(Set &set): set(set) { }
bool operator()(const Entry &entry1, const Entry &entry2) const;
};
bool CustomLess::operator()(
const Entry &entry1, const Entry &entry2) const
{
/* check wether entry1.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry1InSet = false;
for (const Entry &entry : set) {
if ((entry1InSet = entry.first == entry1.first)) break;
}
/* If entry1 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry1InSet) {
for (const Entry &entry : set) {
if (entry.second == entry1.second) return false;
}
}
/* check wether entry2.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry2InSet = false;
for (const Entry &entry : set) {
if ((entry2InSet = entry.first == entry2.first)) break;
}
/* If entry2 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry2InSet) {
for (const Entry &entry : set) {
if (entry.second == entry2.second) return false;
}
}
/* fall back to regular behavior of a less predicate
* for entry1.first and entry2.first
*/
return entry1.first < entry2.first;
}
int main()
{
// Creating & Initializing a map of String & Ints
// with very specific behavior
Set mapOfWordCount({
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
},
CustomLess(mapOfWordCount));
// output
for (const Entry &entry : mapOfWordCount) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
My collaborator would call this a Frankenstein solution and IMHO this is sufficient in this case.
The intention of a std::map/std::set is usually an amortized insert() and find(). This effect is probably totally lost as the CustomLess must iterate (in worst case) over the whole set twice before a value can be returned. (The possible early-outs from iterations in some cases don't help much.)
So, this was a nice puzzle and I solved it somehow but rather to present a counter example.
As #Galik mentioned in the comments, the problem with your code is that the compare function of a map expects two keys as parameters and not key-value pairs. Consequently, you don't have access to the values within the comparator.
Similar to #Scheff, I also don't see a way to make your solution using a custom comparator work in a practical or recommended way. But instead of using a set and a vector, you could also invert your map. The filtering can then be performed by the map::insert() function:
#include <map>
#include <string>
#include <iostream>
int main() {
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = { { "aaa", 10 }, { "ddd", 41 },
{ "bbb", 62 }, { "ccc", 10} };
std::map<int, std::string> inverseMap;
for(const auto &kv : mapOfWordCount)
inverseMap.insert(make_pair(kv.second, kv.first));
for(const auto& kv : inverseMap)
std::cout << "{ \"" << kv.second << "\", " << kv.first << " }" << std::endl;
}
The function map::insert() only inserts an item if its key doesn't exist in the map, yet. Output:
{ "aaa", 10 }
{ "ddd", 41 }
{ "bbb", 62 }
However, if you require your target map setOfWords to be of the type std::map<std::string, int>, then you can invert the inverted map from the code above once again in the following way:
std::map<std::string, int> setOfWords;
for(const auto& kv : inverseMap)
setOfWords[kv.second] = kv.first;
for(const auto& kv : setOfWords)
std::cout << "{ \"" << kv.first << "\", " << kv.second << " }" << std::endl;
As a result (even if this isn't your requirement), setOfWords becomes sorted by the key. Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Code on Ideone

sorting a map based on value

Suppose I have a std::map<std::string,int> this map stores an id along with a debt amount.
I want to know if there was a way for me to obtain the 5 highest (int) values from the map.
I know I could iterate through the map and do a custom sort however is there a custom algorithm that might help me accomplish this ? What would the most efficient way be ?
Only if you save them in another place while inserting to the map and maintain it.
A map is just a map....
Getting the highest 5 will be o(N) on the map. if you manage them while inserting you can do it in o(1)
Create a map std::map<int, std::string> and insert all data from old map into this map.
Then 5 key from end of new map have the highest value.
you could build a vector of iterators into the map:
#include <algorithm>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
// make some data
using data_map_type = map<string, int>;
data_map_type data_map = {
{ "a", 10 },
{ "b", 5 },
{ "c", 3 },
{ "d", 3 },
{ "e", 4 },
{ "f", 1 },
{ "g", 2 },
{ "h", 5 },
{ "i", 0 },
{ "j", 2 },
{ "k", 1 },
};
// build reverse index (initially unordered)
std::vector<data_map_type::const_iterator> second_index;
for(auto it = begin(data_map) ; it != end(data_map) ; ++it)
second_index.push_back(it);
// order the secondary index by descending debt amount
sort(begin(second_index),
end(second_index),
[](const data_map_type::const_iterator& l,
const data_map_type::const_iterator& r)
{
return l->second > r->second;
});
// emit the top 5 (or fewer)
const auto limit = std::min(second_index.size(), size_t(5));
cout << "top " << limit << " entries:" << endl;
for(size_t i = 0 ; i < limit ; ++i)
{
cout << "key=" << second_index[i]->first << ", value=" << second_index[i]->second << endl;
}
return 0;
}

Using Boost Filter Iterator with non-Primitive Objects

Lets say I have a returned iterator from Boost's Multi-Index, where each record contains an age and a name field.
I get the iterator with
auto& index = get<age>(m_records);
auto ibegin = index.begin();
auto iend = index.end();
How could I use this iterator with Boost's Filter Iterator so that I could return all records that, say have name = "John"?
I set up a struct for the predicate operator, I'm not sure if it's right:
struct name_equal_to {
std::string struct_sname;
bool operator()(ServerRecord x) { return x.get_name() == struct_sname; }
name_equal_to(std::string in) : struct_sname(in){}
};
I'd use the filtered adaptor, to make things more readable:
for (auto& r : get<age>(m_records) | filtered(name_equal_to("John"))
std::cout << r << "\n";
I'd stylistically improve the functor (Live On Coliru):
struct name_equal_to {
bool operator()(ServerRecord const& x) const {
return x.get_name() == sname_;
}
name_equal_to(std::string in) : sname_(std::move(in)){}
private:
std::string sname_;
};
And to make it really elegant, use Phoenix to define the predicate named_john in-place:
auto named_john = phx::bind(&record::name, arg1) == "John";
See it Live On Coliru, where it prints two records ordered by the age index:
2 John 40
4 John 57
Full sample code
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/range/iterator_range.hpp>
#include <iostream>
using boost::multi_index_container;
using namespace boost::multi_index;
struct record {
int id;
std::string name;
int age;
friend std::ostream& operator<<(std::ostream& os,const record& e) {
return os << e.id << " " << e.name << " " << e.age;
}
};
typedef multi_index_container<
record,
indexed_by<
ordered_unique<tag<struct id> , BOOST_MULTI_INDEX_MEMBER(record, int , id)> ,
ordered_non_unique<tag<struct name>, BOOST_MULTI_INDEX_MEMBER(record, std::string, name)> ,
ordered_non_unique<tag<struct age> , BOOST_MULTI_INDEX_MEMBER(record, int , age)> >
> employee_set;
employee_set get_records()
{
employee_set es;
es.insert(record{ 0, "Joe", 31 });
es.insert(record{ 1, "Robert", 27 });
es.insert(record{ 2, "John", 40 });
// next insertion will fail, as there is an record with the same ID
es.insert(record{ 2, "Aristotle", 2387 });
es.insert(record{ 3, "Albert", 20 });
es.insert(record{ 4, "John", 57 });
return es;
}
#include <boost/phoenix.hpp>
#include <boost/range/adaptors.hpp>
namespace phx = boost::phoenix;
using namespace phx::arg_names;
using boost::adaptors::filtered;
int main()
{
auto const m_records = get_records();
auto named_john = phx::bind(&record::name, arg1) == "John";
for (auto& r : get<age>(m_records) | filtered(named_john)) {
std::cout << r << "\n";
};
}
#sehe's answer is absolutely correct, but if you care about efficiency then this is not the fastest way to retrieve all Johns sorted by age. Suppose your container has N elements and there are M Johns among them. So:
Traversing all elements sorted by age and filtering by "John" takes N steps.
Retrieving all Johns and sorting by age (as discussed in your other post) requires (roughly) 2·log(N) steps to retrieve the range of Johns, one allocation of the temporary vector, M steps to fill it, O(M·log(M)) to sort it and M to traverse it.
So, it is O(N) vs. O(log(N)+M·log(M)). I bet #2 is faster than #1 when M << N, which I take it to be the usual situation (of course you should measure your actual program).

C++ - Find adjacent elements in std::map

What is the most efficient way to look up the adjacent elements in a STL map using the examples I mention below:
Suppose I have a map of integer - string:
1 -> Test1
5 -> Test2
10 -> Test3
20 -> Test4
50 -> Test5
If I call:
get_adjacent(1) // Returns iterator to 1 and 5
get_adjacent(2) // Returns iterator to 1 and 5
get_adjacent(24) // Returns iterator to 20 and 50
get_adjacent(50) // Returns iterator to 20 and 50
Use std::lower_bound and std::upper_bound for exactly this.
Better yet std::map::equal_range combines the power of both:
See it live on http://liveworkspace.org/code/d3a5eb4ec726ae3b5236b497d81dcf27
#include <map>
#include <iostream>
const auto data = std::map<int, std::string> {
{ 1 , "Test1" },
{ 5 , "Test2" },
{ 10 , "Test3" },
{ 20 , "Test4" },
{ 50 , "Test5" },
};
template <typename Map, typename It>
void debug_print(Map const& map, It it)
{
if (it != map.end())
std::cout << it->first;
else
std::cout << "[end]";
}
void test(int key)
{
auto bounds = data.equal_range(key);
std::cout << key << ": " ; debug_print(data, bounds.first) ;
std::cout << ", " ; debug_print(data, bounds.second) ;
std::cout << '\n' ;
}
int main(int argc, const char *argv[])
{
test(1);
test(2);
test(24);
test(50);
}
Outputs:
1: 1, 5
2: 5, 5
24: 50, 50
50: 50, [end]