hello i got an iterator running on a multimap ,my multimap includes two fields the first 1 is the key value which is a string Lastname and the second 1 is the data which is a pointer to an object called Employee.
in my program i need to be able to find an element inside my map using iterator (cause i am trying to find the data by its ID and not by the key value).
i want to have some sort of indicator that will tell me if the element i am looking for is inside my map or not.
thanks in advance.
and this is my Find code :
MultiMap::iterator iterator;
iterator=m_municipalityMap.begin();
bool flag = false;
while(!(flag) && iterator != m_municipalityMap.end())
{
if(strcmp(iterator->second->GetId() , id) == 0)
{
flag=true;
break;
}
iterator++;
}
if (flag==false)
cout << "could not find Employee"<<endl;
return iterator;
what i need to know if there is a way to know if the iterator stands on nothing like comparing to NULL on pointers
You could use the std::find_if algorithm with a suitable predicate that will handle finding
your Employee based on the ID.
This is an adaption of an example found in "The C++ Standard Library - A Tutorial and Reference" by Nicolai M. Josuttis, Addison-Wesley, 1999:
class value_id_equals
{
private:
string m_id;
public:
// constructor (initialize id to compare with)
value_id_equals (string id)
: m_id(id) { }
// comparison
bool operator() (pair<string, Employee*> elem)
{
return (!strcmp(elem.second->GetID(), m_id.c_str()));
}
};
...
pos = find_if(your_map.begin(),your_map.end(), // linear complexity
value_id_equals(theID));
If you need two keys in the container you could try Boost Multi-index Container.
Another solution is to create two maps, each one with own key, and keep your data in each by (smart) pointers.
Related
I have a QMap<QString, myStruct> with
myStruct {
QString firstname;
QString lastname;
QString status;
}
How can I sort this QMap according to priority order: status then firstname then lastname?
As far as I understand, you'd like to retrieve the values of the map sorted in the mentioned way, but still have access to the key. Right?
Quickly speaking, a map is a collection of <key, value> pairs automatically sorted by key, then you may try a list of <value, key> pairs manually sorted by value instead. Something like QList<QPair<myStruct, QString>>, while overriding the operator< for myStruct.
struct myStruct {
QString firstname;
QString lastname;
QString status;
bool operator<(const myStruct& o) const {
return std::tie(status, firstname, lastname) <
std::tie(o.status, o.firstname, o.lastname);
}
};
QMap<QString, myStatus> map; // your original map
QList<QPair<myStatus, QString>> inv;
// Populate the inverted list
for (auto k : map.keys()) {
inv.append(QPair<myStatus, QString>(map[k], k));
}
std::sort(std::begin(inv), std::end(inv));
for (auto p : inv) {
qDebug() << p.first.status << p.first.firstname << p.first.lastname << p.second;
}
Of course, it is a one-time use structure that doesn't keep updated with your original map, but you mentioned that the map is fixed (constant?) so it may not be a problem then.
BTW, a QMap can be used for the inverse look-up but only in the case the values of the myStruct part are also unique (so they can be used also as a key), otherwise you may overwrite values when constructing the inverse map.
Note: The std::tie is used just to simplify the sorting condition for tuples (so you'd need to include <tuple>).
UPDATE
Answering one of your comments: Yes, you can also specify your own comparison predicate and then avoid overriding the operator<, but I think it is harder to read and less re-usable:
std::sort(std::begin(inv), std::end(inv),
[](const QPair<myStatus, QString>& lhs, const QPair<myStatus, QString>& rhs) {
return std::tie(lhs.first.status, lhs.first.firstname, lhs.first.lastname) <
std::tie(rhs.first.status, rhs.first.firstname, rhs.first.lastname);
});
Of course, you can implement that comparison lambda as you want, I've used the std::tie again to simplify the logic in the post. The downside is that if you need to generate the inverse map in several places you'd have to repeat the lambda expression everywhere (or create a function to create the inverse map of course).
As a side note and in case you are curious, lhs and rhs refers to left-hand side and right-hand side respectively, in this case they are used as lhs < rhs by the sorting algorithm for comparing the elements.
Finally, if you'd want to avoid the std::tie you'd have to make the comparisons manually (code below modifies the operator< of the first version):
bool operator<(const myStruct& o) const {
if (status < o.status) return true;
if (status > o.status) return false;
// status == o.status, move to next attribute
if (firstname < o.firstname) return true;
if (firstname > o.firstname) return false;
// firstname== o.firstname, move to next attribute
if (lastname < o.lastname) return true;
if (lastname > o.lastname) return false;
return false; // are equal
}
You can't sort a QMap manually, you'll have to use a QList (or QVector) for that and use std::sort on it. Use QMap::values() to extract the values (structs) from the map into a list, then implement a compare function/method and call it with std::sort. See cbucharts answer for some hints how to do this.
Keeping map and list in sync when the values change is a different issue, if this is a requirement you should create a separate question, adding a MCVE and more details on what you tried.
Suppose I have the following class
class Human
{
public:
Human();
Human(string,int);
virtual ~Human();
string getName();
protected:
private:
string name;
int staj;
};
I have created list with 2 elements that I pushed in
list<Human> mylist;
Human x ("Mike",13);
Human y("pavlek",33);
I am trying to remove if there is element with name "Mike",I tried removing it like this :
for(list<Human>::iterator it=mylist.begin();it!=mylist.end();++it)
{
if(it->getName()=="Mike")
{
mylist.remove(it);
cout<< "removed";
cout<<it->getName();
}
}
However I get error at passing the value to the remove() function,what should I exactly pass in order to delete this element from the list?
You have simply mistaken erase and remove. According to the C++ reference, remove is used to remove from the list all elements whose values are equals to the given parameter. On the other hand, erase removes a single element given its position or a range of elements given the start and end positions.
If you only need to delete the first element containing "Mike" as its name, simply do something like this:
for(list<Human>::iterator it=mylist.begin();it!=mylist.end();++it)
{
if(it->getName() == "Mike")
{
mylist.erase(it);
break;
}
}
Please notice that after using erase, your iterator will be invalidated. You can circumvent it by using the returned value of erase, which is the next valid iterator value. This detail is important if your list might contain multiple elements whose name is "Mike".
Matheus Portela's solution was the old C++98 method. It's a lot easier now:
mylist.remove_if( [](Human const& h){return h.getName()=="Mike";} );
The condition here is [](Human const& h){return h.getName()=="Mike";}. That is a lambda expression which returns true if the Human h should be removed. You can test any other property or combination of properties there. The { } part of the lambda is a real function body; you could even have for-loops in there:
Other examples:
mylist.remove_if( [](Human const& h){return h.getName().size() > 4; } );
mylist.remove_if( [](Human const& h) {
for (char c: h.getName())
if (c=='i') return true; // remove if name contains an i
return false; } );
Mind you, the latter would be easier with std::any_of.
This question is language-agnostic, but I'm specifically looking for a solution using C++ STL containers. I have a struct like this.
struct User {
int query_count;
std::string user_id;
}
std::multiset<User> users; //currently using
I use a multiset with a comparator that sort on query_count. This allow me to have sorted multiple entries with the same query_count. Now, if I want to avoid duplicates on user_id, I need to scan data and remove the entry and create a new one, taking O(n). I'm trying to think of a way to do this in sub-linear time. I was thinking of a solution based on a map ordered on user_id, but then I would have to scan all the whole data when trying to locate the largest query_count.
EDIT: requirements are insert, delete, update(delete/insert), get highest query_count, find user_id in sub-linear time.
I prefer to use the standard stl containers, but simple modifications are fine. Is there any way to achieve my requirements?
Summary:
The summary of the answers is that to use a ootb solution, I can use boost bi-directional map.
If I'm sticking to STL, then it has to be a combination of using two maps together, updating both carefully, for each insertion of a user.
This sounds like a job for boost's multi_index: http://www.boost.org/doc/libs/1_57_0/libs/multi_index/doc/tutorial/
You can set one index based on the user id to easily prevent duplicates (you insert based on this), and then another sorted index on the query count to easily locate the max.
multi_index from boost is the way to go.
But if you want to use your own DataStructure using basic STL containers, then i suggest you create a class which has two conatiners internally.
keep an itertor to SortedContainer in the map. So that you can delete and access it in O(1)( same as lookup of unordered_map).
X
struct User {
int query_count;
std::string user_id;
}
class UserQueryCountSomething
{
typedef std::list<int> SortedContainer; //better to use a Stack or Heap here instead of list.
SortedContainer sortedQueryCount; //keep the query_count sorted here.
typedef std::pair< User, typename SortedContainer::iterator> UserPosition_T;//a pair of User struct and the iterator in list.
typedef unordered_map < std::string, UserPosition_T > Map_T; // Keep your User struct and the iterator here in this map, aginst the user_id.
Map_T map_;
public:
Insert(User u)
{
//insert into map_ and also in sortedQueryCount
}
int getHighestQueryCount()
{
//return first element in sortedQueryCount.
}
Delete()
{
//find in map and delete.
//get the iterator from the map's value type here.
//delete from the sortedQueryCount using the iteartor.
}
};
}
This can be a starting point for you. Let me know if you more details.
If we just need the highest count, not other ranks of count, then one approach may be to track it explicitly. We may do that as
unordered_map<UserId, QueryCount>;
int max_query_count;
Unfortunately, in some operations, e.g. when the user with max query count is removed, the max value need to freshly computed. Note that, for all other users, whose query count is not maximum, removal of them does not need re-computation of max_query_count. The re-computation, when done, would be O(N), which does not meet the "sub linear" requirement. That may be good enough for many use cases, because the user with maximum query count may not be frequently removed.
However, if we absolutely want to avoid the O(N) re-computation, then we may introduce another container as
multimap<QueryCount, UserId>
to map a specific query count to a collection of users.
In this approach, any mutation operation e.g. add, remove, update, may need to update both the containers. That is little bit of pain, but the gain is that such updates are expected to be logarithmic, e.g. O(lg N), i.e. sub linear.
Update with some code sketch. Note I have used unordered_map and unordered_set, instead of multimap, for count-to-user mapping. Since we do not really need ordering on count, this might be fine; in case if not, unordered_map may be simply changed to map.
class UserQueryCountTracker {
public:
typedef std::string UserId;
typedef int QueryCount;
void AddUser(UserId id) {
int new_count = -1;
auto it = user_count_map_.find(id);
if (it == user_count_map_.end()) { // id does not exist
new_count = 1;
user_count_map_[id] = new_count;
count_user_map_[new_count].insert(id);
}
else { // id exists
const int old_count = it->second;
new_count = old_count + 1;
it->second = new_count;
// move 'id' from old count to new count
count_user_map_[old_count].erase(id);
count_user_map_[new_count].insert(id);
}
assert(new_count != -1);
if (new_count > max_query_count_) {
max_query_count_ = new_count;
}
}
const unordered_set<UserId>& UsersWithMaxCount() const {
return count_user_map_[max_query_count_];
}
private:
unordered_map<UserId, QueryCount> user_count_map_{};
int max_query_count_{0};
unordered_map<QueryCount, unordered_set<UserId>> count_user_map_{};
};
Use bidirectional map, where user id is key and query count is value
#include <map>
#include <utility>
#include <functional>
template
<
typename K, // key
typename V, // value
typename P = std::less<V> // predicate
>
class value_ordered_map
{
private:
std::map<K, V> key_to_value_;
std::multimap<V, K, P> value_to_key_;
public:
typedef typename std::multimap<typename V, typename K, typename P>::iterator by_value_iterator;
const V& value(const K& key) {
return key_to_value_[key];
}
std::pair<by_value_iterator, by_value_iterator> keys(const V& value) {
return value_to_key_.equal_range(value);
}
void set(const K& key, const V& value) {
by_key_iterator it = key_to_value_.find(key);
if (key_to_value_.end() != it) {
std::pair<by_value_iterator, by_value_iterator> it_pair = value_to_key_.equal_range(key_to_value_[key]);
while (it_pair.first != it_pair.second)
if (it_pair.first->first == it->second) {
value_to_key_.erase(it_pair.first);
break;
} else ++it_pair.first;
}
key_to_value_[key] = value;
value_to_key_.insert(std::make_pair(value, key));
}
};
I have a type called Neighbors:
typedef vector<pair<data,int>> Neighbors;
and here's data:
struct data
{
int par[PARAMETERS];
int cluster;
bool visited;
bool noise;
};
I'm trying to write a function that inserts values from _NeighborPts to NeighborPts (but only ones that aren't already in NeighborPts):
void insert_unique(Neighbors* NeighborPts, const Neighbors& _NeighborPts)
{
Neighbors_const_it _it = _NeighborPts.begin();
while(_it != _NeighborPts.end())
{
if(/* _it->first.par isn't in *NeighborPts */)
NeighborPts->push_back(*_it);
++_it;
}
}
and i already have a function equal() which checks if 2 pars are equal.
So do i have to iterate through NeighborPts in a while loop and check if the item is found? or could i use some built-in find or find_if function to do that for me?
You can maintain a sorted vector. Use the lower_bound function from C++ algorithms to locate the insert position each time. If the element at the insert position is equal to the insert element then you have a duplicate.
The performance of this will be pretty good unless the vector grows too large. The point at which you're better off using a set or a unordered_set varies and you'd need to benchmark to find it.
Your current solution with vector will run in O(N^2) time, which is not efficient.
For efficient solution an associative container will be great - such as std::set .
Also you will need to have some "operator less" (instead of "equal ()"), to pass to the function.
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
So you need to provide compare class
struct data_compare {
bool operator() (const data& lhs, const data& rhs) const{
//...
}
};
set<int64_t, data_compare> exising_items;
You may define such a function, or override "operator <" in struct data.
insert all "data" from "_NeighborPts" into a set - O(N*log(N)) time
std::set other_items;
in a loop - iterate _NeighborPts and insert data elements
other_items.insert (_NeighborPts [i]);
std::set my_items;
in a loop - iterate _NeighborPts and insert data elements
my_items.insert (NeighborPts [i]);
Now you need to compare between the 2 sets:
You can do it using std::set_intersection
. or construct a simple loop on the set "my_items"
if the current element in other_items isn't in _my_items, insert it in "NeighborPts"
this solution will run in O(Nlog(N)) time
There is no getting around iterating over the items in _NeighborPts.
As long as you are using an std::vector, there is no getting around the check to determine whether an item is in NeighborPts before inserting in it.
You can make the code a little bit easier to read by using std::for_each and a functor.
struct UniqueItemInserter
{
UniqueItemInserter(Neighbors* neighborsIn) : neighbors(neighborsIn) {}
void operator(pair<data,int> const& item)
{
if ( std::find(neighbors->begin(), neighbors->end(), item) != neighbors->end() )
{
neighbors->push_back(item);
}
}
Neighbors* neighbors;
};
void insert_unique(Neighbors* NeighborPts, const Neighbors& _NeighborPts)
{
std::for_each(_NeighborPts.begin(), _NeighborPts.end(), UniqueItemInserter(NeighborPts));
}
I want to have in a google protocol buffer repeated field only unique elements. In other words, need to use it as a std::set instead of std::vector.
Any idea which is the simplest and most efficient way to do that?
EDIT: I wouldn't want to use any iterators to loop through all the elements if possible.
Ok, as the comments from the question stated, there isn't any way of doing this without using iterators.
However, maybe someone else is interested in this, here is the function i coded to achieve this. This will take as parameters a RepeatedPtrField< T >*(the list) and a std::string(key of the new object that we intend to add to the list) and will return the element that matches the id, or NULL if there isn't any entry with this key in the RepeatedField list.
This way, you can easy keep a list of unique elements directly in a RepeatedField without using any other std structure:
template <class T>
T* repeatedFieldLookup( google::protobuf::RepeatedPtrField< T >* repeatedPtrField, std::string id)
{
google::protobuf::internal::RepeatedPtrOverPtrsIterator<T> it = repeatedPtrField->pointer_begin();
for ( ; it != repeatedPtrField->pointer_end() ; ++it )
{
CommonFields * commonMessage = (CommonFields*) (*it)->GetReflection()->
MutableMessage ((*it), (*it)->GetDescriptor()->FindFieldByName ("common"));
if(commonMessage->id() == id)
{
return *it;
}
}
return NULL;
}
NOTE: in the example above, the proto message will ALWAYS have a field called common(which in my case is also a proto message). You can replace that by anything that you want to make the comparison from your proto messages.
In the case where I had this class:
class Description : public ::google::protobuf::Message {
// ...
inline void add_field(const ::std::string& value);
inline const ::google::protobuf::RepeatedPtrField< ::std::string>& field() const;
// ...
};
I used std::find to only add a value if it didn't exist in the list:
#include <algorithm>
void addField(Description& description, const std::string& value) {
const auto& fields = description.field();
if (std::find(fields.begin(), fields.end(), value) == fields.end()) {
description.add_field(value);
}
}