This is what I am trying right now. I made a comparison function:
bool compare(const std::pair<int, Object>& left, const std::pair<int, Object>& right)
{
return (left.second.name == right.second.name) && (left.second.time == right.second.time) &&
(left.second.value == right.second.value);
}
After I add an element I call std::unique to filter duplicates:
data.push_back(std::make_pair(index, obj));
data.erase(std::unique(data.begin(), data.end(), compare), data.end());
But it seems that this doesn't work. And I don't know what the problem is.
From my understanding std::unique should use the compare predicate.
How should I update my code to make this work ?
I am using C++03.
edit:
I have tried to sort it too, but still doens't work.
bool compare2(const std::pair<int, Object>& left, const std::pair<int, Object>& right)
{
return (left.second.time< right.second.time);
}
std::sort(simulatedLatchData.begin(), simulatedLatchData.end(), compare2);
std::unique requires the range passed to it to have all the duplicate elements next to one another in order to work.
You can use std::sort on the range before you a call unique to achieve that as sorting automatically groups duplicates.
Sorting and filtering is nice, but since you never want any duplicate, why not use std::set?
And while we're at it, these pairs look suspiciously like key-values, so how about std::map?
If you want to keep only unique objects, then use an appropriate container type, such as a std::set (or std::map). For example
bool operator<(object const&, object const&);
std::set<object> data;
object obj = new_object(/*...*/);
data.insert(obj); // will only insert if unique
Related
I have references to 2 maps of type std::map<std::string, int>. I want to create a master list containing all the keys that both maps have in common. My current solution is as follows, but I am curious if there is a more efficient way of approaching this problem?
const std::map<std::string, int>& map1;
const std::map<std::string, int>& map2;
std::vector<std::string> shared_keys;
// only add to master list if both contain the string as a key
for (auto& entry : map1) {
if (map2.find(entry.first) != map2.end()) {
shared_keys.push_back(entry.first);
}
}
It would be nice if I could forgo the for loop entirely / do this as a "one-liner", but not sure how to accomplish that...
std::map is sorted, so you can just use std::set_intersection.
You'll need a custom comparator, since you're only comparing keys ... and then you need an adapter to only use the key in the output iterator ...
A one-liner is pushing it, unless you use something like the Boost.Iterator adapters. Rough sketch (untested):
template <typename K, typename V>
vector<K> map_key_intersection(map<K,V> const &a, map<K,V> const &b)
{
vector<K> result;
using Elem = typename map<K,V>::value_type;
set_intersection(a.begin(), a.end(),
b.begin(), b.end(),
boost::make_function_ouput_iterator(
[&result](Elem const &e) { result.push_back(e.first); }),
[](Elem const& a, Elem const& b) { return a.first < b.first; });
return result;
}
NB, there are several things wrong with this in practice, even apart from the fact that ranges are a better approach if you have access:
The std::map has more than two template parameters. So, add the Compare and Allocator params to your list.
What if they had different Compare types? Now we might not meet the requirements of set_intersection.
What if they have the same Compare type, but were constructed with a stateful comparator that does a different thing for each instance? Weird, but possible ... and we still don't meet the ordering constraint, but it's more expensive to check.
So, to be exactly correct, you should use eg. a.value_comp() instead of the bare operator<, but you also need to be reasonably sure that both maps use the same ordering. At least, you should add a comment to the effect that it's your client's problem if they don't.
You can use std::set_intersection, although as a one liner you will also get values from the one of the maps.
std::vector<std::pair<const std::string, int>> shared;
std::set_intersection(map1.begin(), map1.end(), map2.begin(), map2.end(), std::back_inserter(shared), map1.value_comp());
With C++20's ranges library (or a similar C++11 library), you can grab the keys for the intersection.
std::ranges::set_intersection(std::ranges::keys_view(map1), std::ranges::keys_view(map2), std::back_inserter(shared_keys));
I want to find the max element of vector of pairs.
My criteria is: the max element is one with highest second value of the pair.
I did this:
auto max_angle = std::max_element(begin(angles), end(angles),
[](const std::pair<int, int>& left, const std::pair<int, int>& right){
return left.second < right.second;
});
Is it possible to do it without writing a predicate? Is there any easier way for pairs since it is a std struct?
No you can't, because by default std::pairs are compared lexicographically, meaning element-wise left to right. As such, your solution is the simplest solution you can have.
This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 9 years ago.
I am trying to sort a 2D vector with the type:
vector<pair<char, double>> output;
I am trying to arrange them from the highest to lowest double value and only displaying the top 5. This is what I am trying to do:
sort(output.begin(), output.end());
But this sort is not working properly for me. What am I doing wrong?
By default, std::sort will use the less-than comparison operator for the elements of the container, which will perform am lexicographical comparison using the char first and then the double.
You can use your own ordering function/functor that orders based on the pair's double element only:
bool cmp(const std::pair<char, double>& lhs,
const std::pair<char, double>& rhs)
{
return lhs.second > rhs.second;
}
then
std::vector<std::pair<char, double>> output = ....;
sort(output.begin(), output.end(), cmp);
See working demo here.
As Violet said, you may want to include your own comparison function:
class compare
{
public:
bool operator() (std::pair<char, int> const& p1,
std::pair<char, int> const& p2) const
{
// perform logic here
}
} Predicate;
std::sort uses operator < to compare the elements, and sorts them accordingly. It has an extra optional parameter for the comparsion functor, which we can include like this:
std::sort(output.begin(), output.end(), Predicate);
Note that this can also be done using a lambda in C++11.
Is there a reason you're using a vector of pairs? In other words, does the order in which the elements are stored internally really matter to you? If not, you're probably better off using a map<double,char> with a reverse iterator to get the last 5 elements sorted by double value.
You need to write a comparison operator between the pair<char, double> operands.
http://en.cppreference.com/w/cpp/algorithm/sort
I have a map:
std::map<TyString, int> myMap;
However, in some cases I want to std::map::find an entry by making the comparision TyString == TyStringRef, i.e.
myMap.find(TyStringRef("MyString"));
The reason is that TyString wraps a const char * that it allocates and deallocates by itself.
However, for only finding an entry I don't like to allocate a new string, instead I want to use only the reference (TyStringRef only wraps a const char * without allocating or deallocating memory).
Of course I can just convert the TyStringRef to a TyString, but then I have the memory overhead described above.
Is there an intelligent way to solve this?
Thanks!
Note that std::map::find uses operator< per default, or a user-defined comparison functor. So unless you overload operator< for TyString and TyStringRef, you can't lookup a key in logarithmic time. With operator== being overloaded, you can still lookup in linear time, but not using std::map::find.
For this, you should use a generic algorithm from #include <algorithm>, which is independent from the container classes. It can take any type T and compares it using operator== on the result of operator*() of the iterators you pass in.
std::find(sequence.begin(), sequence.end(), myKey);
However, there is one problem: Since you have a std::map, which uses pairs for the iterators, the key-value-pair will be compared. So you have to use std::find_if, which takes a predicate instead of a value to search for. This predicate should return true for the element you are looking for. You want to have the element (pair) for which first == myKey, so you end up with a code like this:
std::find_if(myMap.begin(), myMap.end(), [](const std::pair<TyString,int> & pair) {
return pair.first == TyStringRef("MyString");
};
This conceptually works, but it won't make use of the binary tree in std::map. So it will take linear time compared to logarithmic time of std::map::find.
There is an alternative, which looks a bit strange in the beginning, but it has the advantage that it will be a logarithmic time lookup. It requires you to overload operator<(TyString,TyStringRef). You can use std::lower_bound to find the first element which is not less (greater or equal) some element with respect to a given comparison function.
std::lower_bound(myMap.begin(), myMap.end(), TyStringRef("MyString"),
[](const std::pair<TyString,int> & entry, const & TyStringRef stringRef) {
return entry.first < stringRef;
}
);
After the "lower bound" was found, you still have to test if the keys compare equal. If they don't, the element was not found. Since it might be possible that all elements compare less with the element you're looking for, so the returned iterator might be the end iterator, which should not be dereferenced. So the full code becomes this, which is analogous to std::map::find and returns the end iterator if the key wasn't found:
template<class Map, class KeyCompareType,
class Iterator = typename Map::const_iterator>
Iterator findInMap(const Map &map, const KeyCompareType &key)
{
typedef typename Map::value_type value_type;
auto predicate = [](const value_type & entry, const KeyCompareType & key) {
return entry.first < key;
};
Iterator it = std::lower_bound(map.begin(), map.end(), key, predicate);
if (it != map.end()) {
if (!(it->first == key))
it = map.end();
}
return it;
}
Live example
You could use STLport, which already does this on its own. Maybe other standardlibrary implementations do the same? Alternatively, you could use std::find(), but that would cost you the logarithmic lookup.
I have this small program that reads a line of input & prints the words in it, with their respective number of occurrences. I want to sort the elements in the map that stores these values according to their occurrences. I mean, the words that only appear once, will be ordered to be at the beginning, then the words that appeared twice 7 so on. I know that the predicate should return a bool value, but I don't know what the parameters should be. Should it be two iterators to the map? If some one could explain this, it would be greatly appreciated. Thank you in advance.
#include<iostream>
#include<map>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::map;
int main()
{
string s;
map<string,int> counters; //store each word & an associated counter
//read the input, keeping track of each word & how often we see it
while(cin>>s)
{
++counters[s];
}
//write the words & associated counts
for(map<string,int>::const_iterator iter = counters.begin();iter != counters.end();iter++)
{
cout<<iter->first<<"\t"<<iter->second<<endl;
}
return 0;
}
std::map is always sorted according to its key. You cannot sort the elements by their value.
You need to copy the contents to another data structure (for example std::vector<std::pair<string, int> >) which can be sorted.
Here is a predicate that can be used to sort such a vector. Note that sorting algorithms in C++ standard library need a "less than" predicate which basically says "is a smaller than b".
bool cmp(std::pair<string, int> const &a, std::pair<string, int> const &b) {
return a.second < b.second;
}
You can't resort a map, it's order is predefined (by default, from std::less on the key type). The easiest solution for your problem would be to create a std::multimap<int, string> and insert your values there, then just loop over the multimap, which will be ordered on the key type (int, the number of occurences), which will give you the order that you want, without having to define a predicate.
You are not going to be able to do this with one pass with an std::map. It can only be sorted on one thing at a time, and you cannot change the key in-place. What I would recommend is to use the code you have now to maintain the counters map, then use std::max_element with a comparison function that compares the second field of each std::pair<string, int> in the map.
A map has its keys sorted, not its values. That's what makes the map efficent. You cannot sort it by occurrences without using another data structure (maybe a reversed index!)
As stated, it simply won't work -- a map always remains sorted by its key value, which would be the strings.
As others have noted, you can copy the data to some other structure, and sort by the value. Another possibility would be to use a Boost bimap instead. I've posted a demo of the basic idea previously.
You probably want to transform map<string,int> to vector<pair<const string, int> > then sort the vector on the int member.
You could do
struct PairLessSecond
{
template< typename P >
bool operator()( const P& pairLeft, const P& pairRight ) const
{
return pairLeft.second < pairRight.second;
}
};
You can probably also construct all this somehow using a lambda with a bind.
Now
std::vector< std::map<std::string,int>::value_type > byCount;
std::sort( byCount.begin(), byCount.end(), PairLessSecond() );