How to add distinct pairs into a set? - c++

I am trying to insert a few pairs of int into a set, these pairs don't have any order; that is, (1,2) = (2,1). So simply I do as follows,
typedef pair<int, int> pairs;
set<pairs> Set;
pair <int,int> myPair;
// adding pairs:
myPair=pair<int,int>(0,1);
pathSet.insert(myPair);
myPair=pair<int,int>(0,2);
pathSet.insert(myPair);
myPair=pair<int,int>(1,0);
pathSet.insert(myPair);
So I end up a set like
(0,1), (0,2) , (1,0)
I want to have
(0,1), (0,2)
How can I avoid duplicate? Is there any way? Is there any better ADT like std::unordered_set in terms of efficiency in comparison with 'set'?

You'll need a custom comparison function. In there, make sure that the order of the elements in a pair do not matter when comparing. A simple way would be to let the first element in the pair always be the smaller one (and exchange first and second otherwise).
The code could look like as follows:
int main() {
typedef pair<int, int> pairs;
auto cmp = [](pairs a, pairs b) {
if (a.first > a.second) {
swap(a.first, a.second);
}
if (b.first > b.second) {
swap(b.first, b.second);
}
return a < b;
};
set<pairs, decltype(cmp)> pathSet(cmp);
pairs myPair=pair<int,int>(0,1);
pathSet.insert(myPair);
myPair=pair<int,int>(0,2);
pathSet.insert(myPair);
myPair=pair<int,int>(1,0);
pathSet.insert(myPair);
cout << pathSet.size();
}
Output:
2

You need a custom compare function for std::set as you are using a std::pair as template type. In C++11, you can make it as a lambda as well.
The idea of compare function is to first check if the Pair is Pair.first < Pair.second, if not, swap to make them in order inside the compare function. This will not change the order of original inserted pair elements, but remove the duplicates as you mentioned.
auto compare = [](pairs lhs, pairs rhs)
{
if(lhs.first > lhs.second ) lhs = pairs{lhs.second, lhs.first };
if(rhs.first > rhs.second ) rhs = pairs{rhs.second, rhs.first };
return lhs< rhs;
};
Something like this: See live here
#include <iostream>
#include <set>
typedef std::pair<int, int> pairs;
int main()
{
auto compare = [](pairs lhs, pairs rhs) //custom compare lambda function
{
if(lhs.first > lhs.second ) lhs = pairs{lhs.second, lhs.first };
if(rhs.first > rhs.second ) rhs = pairs{rhs.second, rhs.first };
return lhs< rhs;
};
std::set<pairs, decltype(compare)> Set(compare);
Set.emplace(std::make_pair(0,1)); // use can also emplace to the Set
Set.emplace(pairs{0,2});
Set.emplace(pairs{1,0});
for(const auto& it: Set)
std::cout << it.first << " " << it.second << std::endl;
}
Output:
0 1
0 2

Related

How can I sort a vector with pairs by the second element in the opposite order if the first elements are equal?

I have a vector with pairs in it like so: vector<pair<int, string>> results; I want to sort the vector first by the first elements (the integers) in descending order. If the first elements are equal, I then want to sort by the second elements in ascending order (alphabetical since they are strings). How can I do this?
As #Lala5th says, you can do:
std::sort (results.begin (), results.end (),
[] (const pair <int, string> &e1, const pair <int, string> &e2)
{ return (e1.first != e2.first) ? e2.first < e1.first :
e1.second < e2.second; });
Using a lambda keeps all the code in one place.
I think I've got the comparator function the right way round.
Use std::sort and pass in a custom comparator. the example below will do what you want.
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
using namespace std;
bool customCompare(pair<int, string> pair1, pair<int, string> pair2) {
if (pair1.first == pair2.first)
{
return pair1.second < pair2.second;
}
return pair1.first > pair2.first;
}
int main() {
vector<pair<int, string> > pairVec;
pairVec.push_back(pair<int, string>(1, "one"));
pairVec.push_back(pair<int, string>(2, "btwo"));
pairVec.push_back(pair<int, string>(2, "atwo"));
pairVec.push_back(pair<int, string>(3, "three"));
sort(pairVec.begin(), pairVec.end(), customCompare);
for (int i = 0; i < pairVec.size(); ++i) {
cout << pairVec[i].first << "\t" << pairVec[i].second << endl;
}
return 0;
}
You should use std::tie when you need to compare multiple members of a pair (or tuple) to each other, taking care to compare members in the order you want:
auto comp = [](auto const &e1, auto const &e2)
{
return std::tie(e2.first, e1.second)
< std::tie(e1.first, e2.second);
};
and you would pass this comparator to std::sort.

How sort function works on vector of pair of pairs of integer?

Given
std::vector<std::pair<std::pair<int,int>, std::pair<int,int>> a;
std::sort(a.begin(),a.end());
How is std::sort function going to sort this type of vector? Is there any generalization for further cascading of pairs?
std::sort uses operator< unless specified otherwise.
According to the documentation of std::pair:
http://en.cppreference.com/w/cpp/utility/pair/operator_cmp
Comparison operators, such as < are defined as the lexicographical equivalents:
Compares lhs and rhs lexicographically, that is, compares the first elements and only if they are equivalent, compares the second elements.
In your case, this logic will be applied recursively at each pair level.
It will sort by first element of the first pair of pairs. In another word, the most left value of pair of pairs.
See this simple example.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> Pair;
typedef pair<Pair, Pair> PPair;
ostream& operator<< (ostream& in, pair<int, int> p)
{
in << '(' << p.first << ", " << p.second << ')';
return in;
}
int main(void)
{
vector<PPair> vPP;
PPair pp1(Pair(123, 2), Pair(8, 7));
PPair pp2(Pair(33, 22), Pair(88, 77));
PPair pp3(Pair(333, 222), Pair(888, 777));
vPP.push_back(pp1);
vPP.push_back(pp3);
vPP.push_back(pp2);
std::sort(vPP.begin(), vPP.end());
vector<PPair>::const_iterator cit;
for (cit = vPP.begin(); cit < vPP.end(); cit++) {
cout << (*cit).first << (*cit).second << endl;
}
return 0;
}
Let our vector be Q
vector <pair<int,pair<int,int>>> Q;
Then for Sorting
sort(Q.begin(),Q.end(),compare);
Compare function result the boolean as per the desired condition
i.e This will sort the vector in
Ascending order
bool compare(pair<int,pair<int,int>> a,pair<int,pair<int,int>> b)
{
if(a.second.first<=b.second.first)
return true;
else
return false;
}
Descending order
bool compare(pair<int,pair<int,int>> a,pair<int,pair<int,int>> b)
{
if(a.second.first>=b.second.first)
return true;
else
return false;
}
So,you can design condition as per your requirement.

C++ map customize comparator

I defined a map to count the number of strings while sorting the strings by their length:
struct cmp {
bool operator()(const string& a, const string& b) {
return a.size() > b.size();
}
};
int main() {
map<string, int, cmp> mp;
mp["aaa"] = 1;
mp["bbb"] = 2;
cout << mp["aaa"];
}
I'm confused as the output is 2. How should I achieve my goal?
Because of the way your comparator is defined, strings "aaa" and "bbb" are considered equal. Your map has one item, not two. First you assigned 1 to that item, then you assigned 2.
To solve the problem, define your comparator as follows:
struct cmp {
bool operator()(const string& a, const string& b) {
return a.size() == b.size() ? a > b : a.size() > b.size();
}
};
That way, the strings will be considered equal only if they actually are equal, not only when their sizes match, but the string length will still have priority for sorting.
std::map not only sorts items by key, it stores them by (unique) key - 1 item per key.
This behavior is defined by the comparator: if for keys a & b neither of a<b and b<a is true, these keys are considered equal.
In your case mp["bbb"] = 2 just overwrites mp["aaa"].
If you want to fit all the strings in the map, you can use std::multimap, which allows more than 1 value per key.
The other way is to redefine the comparator, so that it would take the different strings into account:
struct cmp {
bool operator()(const string& a, const string& b) {
if(a.size() < b.size()) return true;
if(b.size() < a.size()) return false;
return a<b;
}
};
Thus your map will still prioritize sorting by string length, but it will also distinguish different strings of same size.
Depending on your use case, you can also check other containers like priority_queue or just plain vector with a proper insertion technique.
If you want to allow strings of identical size in your map but do not care about their relative order, then std::multimap is an alternative solution:
#include <map>
#include <iostream>
#include <string>
struct cmp {
bool operator()(const std::string& a, const std::string& b) const {
return a.size() > b.size();
}
};
int main() {
std::multimap<std::string, int, cmp> mp;
mp.emplace("eee", 5);
mp.emplace("aaa", 1);
mp.emplace("bbb", 2);
mp.emplace("cccc", 3);
mp.emplace("dd", 4);
auto const elements_of_size_3 = mp.equal_range("aaa");
for (auto iter = elements_of_size_3.first; iter != elements_of_size_3.second; ++iter)
{
std::cout << iter->first << " -> " << iter->second << '\n';
}
}
Output:
eee -> 5
aaa -> 1
bbb -> 2
From std::multimap<std::string, int, cmp>'s point of view, "eee", "aaa" and "bbb" are all completely equal to each other, and std::multimap allows different keys to be equal. Their relative order is actually guaranteed to be the order of insertion since C++11.

Implementation of lower_bound on vector pairs

I know we need to include some compare function in order to achieve this.
But not able to write for this one.
For example:
Elements of vector={(2,4),(4,2),(5,1),(5,3)}
to find=5
lower_bound() should return 2
code->
#define pp pair<int,int>
bool cmp(const pp &l,const pp &r) {
return l.first < r.first;
}
int main() {
vector<pp> v;
sort(v.begin(), v.end(), cmp);
int id=(int)(lower_bound(v.begin(), v.end(), ??) - v.begin());
}
Pairs (just like tuples) compare lexicographically anyway. You don't need to define any special comparators for this.
And since you're using lower_bound you'll be searching for the first element that does not compare less than the val you're searching, so you should use a min value as the second pair element. To sum up, all can be done in "two" lines of code :
sort(v.begin(),v.end());
auto id = distance(v.begin(), lower_bound(v.begin(),v.end(),
make_pair(5, numeric_limits<int>::min())) );
Some Notes :
Use std::distance to calculate the number of elements between two iterators
The return type of std::distance is an unsigned type. Unless you need negative indexing (Python like syntax for "count from the end" indexes) it's a good practice to keep your indexes unsigned.
Since you don't care about the second value of pp, just construct a temporary pp object with any value as the second element.
int id = std::lower_bound(v.begin(), v.end(), pp(5, 0), cmp) - v.begin();
I think you should compare the pairs as per definition of lower_bound
So,
typedef pair<int,int> pp;
//...
int id=(int)(lower_bound(v.begin(),v.end(),
pp(5,std::numeric_limits<int>::min())), //Value to compare
[](const pp& lhs, const pp& rhs) // Lambda
{
return lhs < rhs ; // first argument < second
}
) - v.begin()
);
You can use lower_bound on vector of pairs with custom compare operator .
You need to pass four arguments in that case like this :-
it1 = iterator position from where to search
it2 = iterator position till where to search
lower_bound (it1 ,it2 , finding_element, your_comparator )
auto myComp = [&](pair<int,string> e1, pair<int,string> e2) {
if(e1.second!=e2.second)
return e1.second<e2.second;
else
return e1.first<e2.first;
};
void Show_sample_code()
{
vector<pair<int,string>> data={{1, "sahil"}, {2, "amin"}};
sort(data.begin(), data.end(), myComp);
pair<int, string> p={1,"sahil"};
auto it=lower_bound( data.begin(), data.end(), p, myComp ) ;
if(it!=data.end())
cout<<"found at index="<<distance(data.begin(), it)<<endl;
else
cout<<"notfound"<<endl;
return;
}

How can i get the top n keys of std::map based on their values?

How can i get the top n keys of std::map based on their values?
Is there a way that i can get a list of say for example the top 10 keys with the biggest value as their values?
Suppose we have a map similar to this :
mymap["key1"]= 10;
mymap["key2"]= 3;
mymap["key3"]= 230;
mymap["key4"]= 15;
mymap["key5"]= 1;
mymap["key6"]= 66;
mymap["key7"]= 10;
And i only want to have a list of top 10 keys which has a bigger value compared to the other.
for example the top 4 for our mymap is
key3
key6
key4
key1
key10
note:
the values are not unique, actually they are the number of occurrences of each key. and i want to get a list of most occurred keys
note 2:
if map is not a good candidate and you want to suggest anything, please do it according to the c++11 ,i cant use boost at the time.
note3:
in case of using std::unordered_multimap<int,wstring> do i have any other choices?
The order of a map is based on its key and not its values and cannot be reordered so it is necessary to iterate over the map and maintain a list of the top ten encountered or as commented by Potatoswatter use partial_sort_copy() to extract the top N values for you:
std::vector<std::pair<std::string, int>> top_four(4);
std::partial_sort_copy(mymap.begin(),
mymap.end(),
top_four.begin(),
top_four.end(),
[](std::pair<const std::string, int> const& l,
std::pair<const std::string, int> const& r)
{
return l.second > r.second;
});
See online demo.
Choosing a different type of container may be more appropriate, boost::multi_index would be worth investigating, which:
... enables the construction of containers maintaining one or more indices with different sorting and access semantics.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, const char * argv[])
{
map<string, int> entries;
// insert some random entries
for(int i = 0; i < 100; ++i)
{
string name(5, 'A' + (char)(rand() % (int)('Z' - 'A') ));
int number = rand() % 100;
entries.insert(pair<string, int>(name, number));
}
// create container for top 10
vector<pair<string, int>> sorted(10);
// sort and copy with reversed compare function using second value of std::pair
partial_sort_copy(entries.begin(), entries.end(),
sorted.begin(), sorted.end(),
[](const pair<string, int> &a, const pair<string, int> &b)
{
return !(a.second < b.second);
});
cout << endl << "all elements" << endl;
for(pair<string, int> p : entries)
{
cout << p.first << " " << p.second << endl;
}
cout << endl << "top 10" << endl;
for(pair<string, int> p : sorted)
{
cout << p.first << " " << p.second << endl;
}
return 0;
}
Not only does std::map not sort by mapped-to value (such values need not have any defined sorting order), it doesn't allow rearrangement of its elements, so doing ++ map[ "key1" ]; on a hypothetical structure mapping the values back to the keys would invalidate the backward mapping.
Your best bet is to put the key-value pairs into another structure, and sort that by value at the time you need the backward mapping. If you need the backward mapping at all times, you would have to remove, modify, and re-add each time the value is changed.
The most efficient way to sort the existing map into a new structure is std::partial_sort_copy, as (just now) illustrated by Al Bundy.
since the mapped values are not indexed, you would have to read everything and select the 10 biggest values.
std::vector<mapped_type> v;
v.reserve(mymap.size());
for(const auto& Pair : mymap)
v.push_back( Pair.second );
std::sort(v.begin(), v.end(), std::greater<mapped_type>());
for(std::size_t i = 0, n = std::min<int>(10,v.size()); i < n; ++i)
std::cout << v[i] << ' ';
another way, is to use two maps or a bimap, thus mapped values would be ordered.
The algorithm you're looking for is nth_element, which partially sorts a range so that the nth element is where it would be in a fully sorted range. For example, if you wanted the top three items in descending order, you'd write (in pseudo C++)
nth_element(begin, begin + 3, end, predicate)
The problem is nth_element doesn't work with std::map. I would therefore suggest you change your data structure to a vector of pairs (and depending on the amount of data you're dealing with, you may find this to be a quicker data structure anyway). So, in the case of your example, I'd write it like this:
typedef vector<pair<string, int>> MyVector;
typedef MyVector::value_type ValueType;
MyVector v;
// You should use an initialization list here if your
// compiler supports it (mine doesn't...)
v.emplace_back(ValueType("key1", 10));
v.emplace_back(ValueType("key2", 3));
v.emplace_back(ValueType("key3", 230));
v.emplace_back(ValueType("key4", 15));
v.emplace_back(ValueType("key5", 1));
v.emplace_back(ValueType("key6", 66));
v.emplace_back(ValueType("key7", 10));
nth_element(v.begin(), v.begin() + 3, v.end(),
[](ValueType const& x, ValueType const& y) -> bool
{
// sort descending by value
return y.second < x.second;
});
// print out the top three elements
for (size_t i = 0; i < 3; ++i)
cout << v[i].first << ": " << v[i].second << endl;
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cassert>
#include <iterator>
using namespace std;
class MyMap
{
public:
MyMap(){};
void addValue(string key, int value)
{
_map[key] = value;
_vec.push_back(make_pair(key, value));
sort(_vec.begin(), _vec.end(), Cmp());
}
vector<pair<string, int> > getTop(int n)
{
int len = min((unsigned int)n, _vec.size());
vector<Pair> res;
copy(_vec.begin(), _vec.begin() + len, back_inserter(res));
return res;
}
private:
typedef map<string, int> StrIntMap;
typedef vector<pair<string, int> > PairVector;
typedef pair<string, int> Pair;
StrIntMap _map;
PairVector _vec;
struct Cmp:
public binary_function<const Pair&, const Pair&, bool>
{
bool operator()(const Pair& left, const Pair& right)
{
return right.second < left.second;
}
};
};
int main()
{
MyMap mymap;
mymap.addValue("key1", 10);
mymap.addValue("key2", 3);
mymap.addValue("key3", 230);
mymap.addValue("key4", 15);
mymap.addValue("key6", 66);
mymap.addValue("key7", 10);
auto res = mymap.getTop(3);
for_each(res.begin(), res.end(), [](const pair<string, int> value)
{cout<<value.first<<" "<<value.second<<endl;});
}
The simplest solution would be to use std::transform to build
a second map:
typedef std::map<int, std::string> SortedByValue;
SortedByValue map2;
std::transform(
mymap.begin(), mymap.end(),
std::inserter( map2, map2.end() ),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
Then pick off the last n elements of map2.
Alternatively (and probably more efficient), you could use an
std::vector<std::pair<int, std::string>> and sort it
afterwards:
std::vector<std::pair<int, std::string>> map2( mymap.size() );
std::transform(
mymap.begin(), mymap.end()
map2.begin(),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
std::sort( map2.begin(), map2.end() );
(Note that these solutions optimize for time, at the cost of
more memory.)