C++ Sort case insensitive vector pair - c++

I have the following vector
std::vector< std::pair<std::string, std::pair<std::string, std::string> > > vecList;
In the loop I add the entry:
vecList.push_back(std::make_pair(name.toStdString(), std::make_pair(path.toStdString(), arguments.toStdString())));
I want to call:
std::sort(vecList.begin(), vecList.end(), CompareSort);
bool SettingsData::CompareSort(const std::pair <std::string, std::pair<std::string, std::string> > &first,
const std::pair< std::string, std::pair<std::string, std::string> > &second)
{
unsigned int i=0;
/*
while ( (i < first.length()) && (i <second.length()) )
{
if (tolower(first[i]) < tolower(second[i]))
return true;
else if (tolower(first[i]) > tolower(second[i]))
return false;
++i;
}
return ( first.length() < second.length() );
*/
return true;
}
I want to sort by the first pair (name) string, but when I get a compile error:
error: no matching function for call to 'sort(std::vector<std::pair<std::basic_string<char>, std::pair<std::basic_string<char>, std::basic_string<char> > > >::iterator, std::vector<std::pair<std::basic_string<char>, std::pair<std::basic_string<char>, std::basic_string<char> > > >::iterator, <unresolved overloaded function type>)'
std::sort(vecList.begin(), vecList.end(), CompareSort);
Can anyone give a tip on what I can be doing wrong
Updated Fix
In my header file I just set the declaration to static:
static bool CompareSort(const std::pair< std::string, std::pair<std::string, std::string> > &first,
const std::pair< std::string, std::pair<std::string, std::string> > &second);
Thanks to Joachim suggestion

The most likely reason is that CompareSort is a non-static member function, which means it actually have a hidden first argument (the this pointer). If you can make it static it should work okay. Otherwise you can make the function a non-member function, or use std::bind.

Related

Why C++ returns error when I use my custom constructor in priority queue?

priority_queue<pair<int, int>, vector<pair<int, int>>, comparator> pq;
static bool comparator(pair<int, int> &m, pair<int, int> &n) {
if (m.second < n.second) return true;
else return false;
}
Error:
I defined my comparator inside a class, and try to declare the priority queue inside another function in the same class, and the error happens.
The third template parameter to std::priority_queue is a type.
comparator is not a type. It is a function.
But before you can fix this, there are two more problems to fix:
types and object must be declared before they are used, and the shown code does not declare comparator before using it.
comparators must taken their parameters as const references, they are not allowed to modify them.
Therefore, your comparator should be:
static bool comparator(const pair<int, int> &m, const pair<int, int> &n)
Its type is: bool (*)(const pair<int, int> &, const pair<int, int> &)
That's what the third parameter to std::priority_queue template should be, and then the actual comparator must be passed to the object's constructor, std::priority_queue has an overloaded constructor that takes an instance of the comparator as a parameter:
static bool comparator(const pair<int, int> &m, const pair<int, int> &n) {
if (m.second < n.second) return true;
else return false;
}
priority_queue<pair<int, int>, vector<pair<int, int>>,
bool (*)(const pair<int, int> &, const pair<int, int> &)
> pq{comparator};
However, you will discover that most of this is unnecessary, and I showed it only for pedanticty's purposes. std::priority_queue's default comparator template parameter will work just fine, because std::pair implements operator<:
priority_queue<pair<int, int>, vector<pair<int, int>>> pq{comparator};
The third template argument for template type parameter must be a type, whereas comparator is a function. You may get the type of the function with decltype(&comparator).
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, decltype(&comparator)> pq(comparator);
The last cppreference example with lambda is very similar to your code https://coliru.stacked-crooked.com/view?id=86f63b09847ba22e.

sorting a vector of std::pair<int, std::unique_ptr<const T> > depending on pair.first

I'm trying to sort a vector of pairs containing a smart pointers of a const object. I'm trying to sort only depending on the first object. Below you see (one of my numerous attempts to write) code that is supposed to do this, alongside with an excerpt from the error.
The compiler complains about the lambda parameters. I've tried to make the parameters const, non-const, references, rvalue-references, to no avail. Help please?
std::pair<int, std::unique_ptr<const std::string> > a;
auto uniq = std::make_unique<const std::string>("hurz");
a = std::make_pair(1,std::move(uniq));
std::pair<int, std::unique_ptr<const std::string> > b;
uniq = std::make_unique<const std::string>("hurz");
b = std::make_pair(2,std::move(uniq));
std::vector<std::pair<int,std::unique_ptr<const std::string> > > vec;
vec.push_back(std::move(a));
vec.push_back(std::move(b));
std::sort(std::make_move_iterator(vec.begin()),
std::make_move_iterator(vec.end()),
[]
(const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
The error message didn't help me:
error: no matching function for call to
'swap(std::move_iterator<__gnu_cxx::__normal_iterator<std::pair<int,
std::unique_ptr<const std::basic_string<char> > >*, std::vector<std::pair<int,
std::unique_ptr<const std::basic_string<char> > > > > >::value_type,
std::move_iterator<__gnu_cxx::__normal_iterator<std::pair<int,
std::unique_ptr<const std::basic_string<char> > >*, std::vector<std::pair<int,
std::unique_ptr<const std::basic_string<char> > > > > >::value_type)' swap(*__a,
*__b);
candidates are:
/usr/include/c++/4.9/bits/move.h:166:5: note: void std::swap(_Tp&, _Tp&)
[with _Tp = std::pair<int, std::unique_ptr<const std::basic_string<char> > >]
plus many more errors in the same vein
The issue here is the use of std::make_move_iterator. When you do so you turn the iterators into move_iterator's which means when you dereference them you get a T&&, instead of a T& like you do with normal iterators.
std::swap, which is used in your implementation of std::sort only takes lvalue references so it cannot bind to the dereferenced iterator. If you use
std::sort(vec.begin(),
vec.end(),
[]
(const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
instead then you will have lvalues for std::swap to bind to and std::swap will work for move only types
I add some further explanation to the answer of #NathanOliver which is too long for a comment. I guess the OP's idea of using
std::sort(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()),
[](const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
i.e., with a move_iterator applied to vector.begin(), is to use move assignments (and not copies) inside the sorting routine. This idea is tempting, but it's not necessary because inside std::sort, assignments are usually done with std::swap which tries to move arguments passed by reference.
On the other hand, for algorithms which use input and output iterators, such as most basically std::copy or std::copy_if, the use of std::make_move_iterator can be quite useful. Those often use constructs like *output_it = *input_it, and together with std::make_move_iterator this corresponds to *output_it = std::move(*input_it), so the move-assignment operator of the type dereferenced by *output_it could be used.

Accessing a vector which is a value in a map to run the equality operator on it with another vector

I have a vector that is made up of maps which have the following type definition:
std::map< int, std::vector<int> >
Thus, the definition of the vector itself is:
std::vector< std::map< int, std::vector<int> > >
Now I am creating a vector of integers and inserting it in a map with an integer key and adding the map itself to the outer vector:
std::map<int, std::vector<int> > vDescriptorAtom;
vDescriptorAtom.insert( std::make_pair(498, vListOfIds) );
messageDescriptorVector.push_back( vDescriptorAtom );
Where vListOfIds itself is a vector of integers.
At a later stage I want to extract the inner vector and compare with another vector that I possess. I was under the impression that I can easily use the == operator between the two vectors to do the comparison. But I am having some trouble doing this.
I tried:
for ( int i = 0 ; i <= messageDescriptorVector.size(); i++ )
{
if ( current_tag_stack == (messageDescriptorVector.at(i))->second )
{
vFoundMatchingDescriptor = true;
break;
}
}
Note that current_tag_stack has the following definition:
std::vector<int> current_tag_stack;
But I am getting this error:
base operand of â->â has non-pointer type âstd::map<int, std::vector<int, std::allocator<int> >, std::less<int>, std::allocator<std::pair<const int, std::vector<int, std::allocator<int> > > > >â
What am I doing wrong ?
EDIT
As suggested by a comment, I tried to access the vector that I have stored inside the map as: messageDescriptorVector.at(i).second but it is giving me this error: âclass std::map<int, std::vector<int, std::allocator<int> >, std::less<int>, std::allocator<std::pair<const int, std::vector<int, std::allocator<int> > > > >â has no member named âsecondâ
messageDescriptorVector.at(i) is a map. You have to obtain an element of that map, in order to get it's second and compare it with your reference vector. So you need one more level of iteration.
for ( int i = 0 ; i < messageDescriptorVector.size(); i++ )
{
auto& m = messageDescriptorVector.at(i); // m is a map<int, vector<int>>
for (auto& p : m) // p is a ref to pair<int, vector<int>>
{
if ( current_tag_stack == p.second )
{
// do something ....
or just
for (auto& m : messageDescriptorVector)
{
for (auto& p : m)
{
if ( current_tag_stack == p.second )
{
// do something ....
Aside of issue that you are trying to accessing member second on a map, I would recommend to use typedefs in your code. It will not only simplify your code, but will help to change data structure if necessary. In your case all of this definitions all around your code:
std::map< int, std::vector<int> >
std::vector< std::map< int, std::vector<int> > >
std::map<int, std::vector<int> > vDescriptorAtom;
std::vector<int> current_tag_stack;
Compare it with:
typedef std::vector<int> tag_stack;
typedef std::map<int,tag_stack> tag_stack_map;
tag_stack_map vDescriptorAtom;
tag_stack current_tag_stack;
And imagine one day you would need to change your tag_stack from std::vector to something else. Where it will be easier?
function at() returns a reference to the element at specified location pos, so you should write:
(messageDescriptorVector.at(i)).second
I preferred write this code like:
std::map<int, std::vector<int>> vDescriptorAtom;
std::vector<std::map<int, std::vector<int>>> messageDescriptorVector;
vDescriptorAtom[498] = vListOfIds;
messageDescriptorVector.push_back( vDescriptorAtom );
// do something
for(auto& maps : messageDescriptorVector)
for(auto& pairs : maps)
if (current_tag_stack == pairs.second)
// do something

Can't access a map member from a pointer

That's my first question :)
I'm storing the configuration of my program in a Group->Key->Value form, like the old INIs. I'm storing the information in a pair of structures.
First one, I'm using a std::map with string+ptr for the groups info (the group name in the string key). The second std::map value is a pointer to the sencond structure, a std::list of std::maps, with the finish Key->Value pairs.
The Key->Value pairs structure is created dynamically, so the config structure is:
std::map< std::string , std::list< std::map<std::string,std::string> >* > lv1;
Well, I'm trying to implement two methods to check the existence of data in the internal config. The first one, check the existence of a group in the structure:
bool isConfigLv1(std::string);
bool ConfigManager::isConfigLv1(std::string s) {
return !(lv1.find(s)==lv1.end());
}
The second method, is making me crazy... It check the existence for a key inside a group.
bool isConfigLv2(std::string,std::string);
bool ConfigManager::isConfigLv2(std::string s,std::string d) {
if(!isConfigLv1(s))
return false;
std::map< std::string , std::list< std::map<std::string,std::string> >* >::iterator it;
std::list< std::map<std::string,std::string> >* keyValue;
std::list< std::map<std::string,std::string> >::iterator keyValueIt;
it = lv1.find(s);
keyValue = (*it).second;
for ( keyValueIt = keyValue->begin() ; keyValueIt != keyValue->end() ; keyValueIt++ )
if(!((*keyValueIt).second.find(d)==(*keyValueIt).second.end()))
return true;
return false;
}
I don't understand what is wrong. The compiler says:
ConfigManager.cpp||In member function ‘bool ConfigManager::isConfigLv2(std::string, std::string)’:|
ConfigManager.cpp|(line over return true)|error: ‘class std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >’ has no member named ‘second’|
But it has to have the second member, because it's a map iterator...
Any suggestion about what's happening?
Sorry for my English :P, and consider I'm doing it as a exercise, I know there are a lot of cool configuration managers.
keyValueIt is not a map iterator, it is a list iterator.
You can just do
if (keyValueIt->find(d) != keyValueIt->end())
I think Joel is correct with
if (keyValueIt->find(d) != keyValueIt->end())
However I wanted to encourage you use some typedefs to try and simplify your code. Using typedefs can help when diagnosing problems like this (and if you're lucky your compiler will give you more meaningful error messages as a result.
For instance:
typedef std::map<std::string,std::string> KeyValueMap;
typedef std::list< KeyValueMap > ConfigurationList;
typedef std::map< std::string, ConfigurationList* > ConfigurationMap;
bool isConfigLv2(std::string,std::string);
bool ConfigManager::isConfigLv2(std::string s,std::string d) {
if(!isConfigLv1(s))
return false;
ConfigurationMap::iterator it;
ConfigurationList* keyValue;
ConfigurationList::iterator keyValueIt; // <- it's not a keyValue iterator, it's a ConfigList iterator!
it = lv1.find(s);
keyValue = (*it).second;
for ( keyValueIt = keyValue->begin() ; keyValueIt != keyValue->end() ; keyValueIt++ )
if(!((*keyValueIt).second.find(d)==(*keyValueIt).second.end()))
return true;
return false;
}
Simplifying the types makes it more obvious to me that keyValueIt is probably being misued (i.e. it's actually a list iterator, and not a KeyValueMap iterator and so the '.second' access is erroneous.)
If you just want a group/key/value structure you are over-complicating it, you have one more level in your data structure then needed.
The additional list isn't needed, a map of maps is sufficient:
// typedefs for readability:
typedef std::map<std::string, std::string> Entries;
typedef std::map<std::string, Entries> Groups;
// class member:
Groups m_groups;
bool ConfigManager::hasKey(const std::string& group, const std::string& key)
{
Groups::const_iterator it = m_groups.find(group);
if(it == m_groups.end())
return false;
const Entries& entries = it->second;
return (entries.find(key) != entries.end());
}

find() problems

I've got an error while using find() function. Here is the code:
#include <iostream>
#include <map>
#define N 100000
using namespace std;
int main (int argc, char * const argv[]) {
map<int,int> m;
for (int i=0; i<N; i++) m[i]=i;
find(m.begin(), m.end(), 5);
return 0;
}
I'm getting an compiller error:
error: no match for 'operator==' in '__first. __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::operator* [with _Iterator = std::_Rb_tree_iterator<std::pair<const int, int> >, _Sequence = __gnu_debug_def::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >]() == __val'
Including 'algorithm' nothing changes. Compiling in VS2008 shows similar error.
I know about m.find(), but I realy need to use find() too.
Thanks a lot for your assistance!
P.S. Actualy, the task is to compare speed of m.find(5) and find(m.begin(), m.end(), 5), so I need to make both of them work properly.
begin() and end() on all STL containers provide access to elements of those collections. Type of those elements is known as value_type of the container. For std::map<Key, Value>, its value_type is std::pair<Key, Value>. Therefore, your find function is trying to find a pair<int, int> which is equal to 5. Since there's no operator== defined to compare pair<int, int> and int, you get the error.
The correct way to do this (so long as you want to avoid member find()) is to use std::find_if:
template <class First>
struct first_equal
{
const First value;
first_equal(const First& value)
: value(value)
{
}
template <class Second>
bool operator() (const std::pair<First, Second>& pair) const
{
return pair.first == value;
}
};
...
find_if(m.begin(), m.end(), first_equal<int>(5));
You could also overload operator== for pair and int to do what you want, but it's a very hackish way (because it will affect all your code, and because such a comparison has no meaning in general).
find() requires a parameter that can be compared to *iterator. For your map, this will be pair<int,int>. You'll need to create a dummy pair, plus a comparison functor to compare the pairs.
Just use m.find(5)