Suppose I have a struct SignalError which has an element "errName" and many other elements:
typedef struct SignalError
{
QString errName;
.....
};
I create QList of this struct:
QList<SignalError> signalErrList;
I will append the struct element to the QList using append call.
SignalError sgErr1 = {"Error_1"};
signalerrList.append(sgErr1);
Before appending the element to the list I want to check if there is any element with the same name "errName" already existing in the QList or not. If it is then I will not add the element to the list.
How do I do this?
Also, should I create a list of objects like:
QList<SignalError> signalErrList;
or create list of pointer to the object:
QList<SignalError*> signalErrList;
You should use QList<SignalError> signalErrList; not a QList of pointers.
If you don’t care about order, you should use a std::set<SignalError> which will give you deduplication automatically.
If you care about order and the O(N) search isn’t a problem, then use QList or std::vector and do
if (auto it = std::find(signalErrList.begin(), signalErrList.end(), sgErr1;
it == signalErrList.end()) {
signalErrList.push_back(std::move(sgErr1));
}
which you could (and should) name as a function:
//! A linear-time (O(n)) search followed by push-back if not found
//! API modeled on https://en.cppreference.com/w/cpp/container/set/emplace
template <typename Container, typename Value>
std::pair<typename Container::iterator, bool>
emplaceBackLinearUnique(Container& c, Value&& v) {
if (auto it = std::find(c.begin(), c.end(), v); it != c.end()) {
// Return iterator to the found one and say that it wasn't emplaced.
return { it, false };
}
// Not found:
c.emplace_back(std::forward<Value>(v));
// Return iterator to the new last element and report that it was emplaced:
return { c.begin() + c.size() - 1, true };
}
which lets you replace. your use case with this.:
emplaceBackLinearUnique(signalErrList, sgErr1);
Related
I want to try reversing a forward_list (without the reverse() method) but I'm not sure how to get rid of the last element after the erase_after() method without using resize().
#include<forward_list>
template<typename vt>
class reverse_forward_list{
public:
static void reverse(std::forward_list<vt>& list){
typename std::forward_list<vt>::iterator iter = list.begin();
int x = 0;
for(vt& i :list){
list.emplace_front(i);
x++;
}
list.erase_after(iter,list.end());
//list.resize(x);
}
};
Any help is appreciated
Use list.before_begin() instead to also erase the previous first element.
Also, this can be implemented more efficiently by splicing each node to the front of the list (which is probably how reverse works internally, and you won't have to allocate new nodes/construct new values)
IMHO there should be a VERY good reason for manipulating a list while traversing it... It's a disaster waiting to happen.
Instead, consider using a temporary list, something like that:
template<typename vt>
static void reverse(std::forward_list<vt>& list) {
std::forward_list<vt> newlist;
while (!list.empty()) {
newlist.push_front(list.front());
list.pop_front();
}
list = newlist;
}
I have this class:
class test_t {
public:
int value;
test_t() { }
test_t(int _value) : value(_value) { }
};
Now I have create a unordered_map with the a int value as key
std::unordered_map<int, test_t> map;
When I will use the operator [] if the key it not exist a new element will be add to the map calling the construct.
test_t & test = map[0];
Now it si possible to tell to the unordered_map to call the other constructor?
i.e. is is possibile to do something like this?
std::unordered_map<int, test_t(5)> map;
in the means that every new element will be create whit the construction with value 5?
I know that i can create a construction like this:
test_t(int _value = 5) { }
however the class test is only a example of something more complex.
[] operator value initialize the mapped value if it didn't find it and you cannot change it. You can change your default initializer though.
test_t() { value = 5;}
If you want to insert the value of your choice in case if the key is not in the map, one way would be using find to get an iterator to the key value pair and if the iterator was end iterator, then insert your key value pair.
Optionally as #PaulMcKenzie suggested, you can just use insert, as "it returns a pair consisting of an iterator to the inserted element (or to the element that prevented the insertion) and a bool denoting whether the insertion took place."
m.insert({key, test_t(5)});
in java, I sometimes do this
Map<String, POJO> objmap = new HashMap<String, POJO>();
POJO obj = null;
if ((obj = objMap.get(key)) == null) {
obj = new POJO();
objMap.put(key, obj);
}
obj.setName("something");
obj.setAddress("yeah");
What is the best practice to do similar thing in c++ with std::map?
to create a obj in map if not exist, then update its properties?
Like this:
void insert_or_update(const K & k, const T & t, std::map<K, T> & m)
{
auto p = m.insert(std::make_pair(k, t));
if (!p.second) p.first->second = t;
}
Or:
m[k] = t;
The latter requires T to be default-constructible and assignable.
In C++17 you can also say:
m.insert_or_assign(k, t);
This has fewer restrictions than the above construction and returns information on whether the insertion took place, as well as the iterator to the element.
You want to use the insert function, it returns an iterator and a boolean regarding whether a new object was inserted:
something like this:
typedef map<int,void*> M;
M m;
auto insertion = m.insert(M::value_type(0,nullptr));
if (insertion.second) {
insertion.first->second = new... (// allocate your item or whatever, this is the iterator to it)
}
You can write objmap[key] = value.
See: http://www.cplusplus.com/reference/map/map/operator[]/
std::map<std::string, POJO> mapStr2Pojo;
mapStr2Pojo["something"].setName("something");
mapStr2Pojo["something"].setAddress("yeah");
std::map<>'s operation[] inserts the object if it doesn't find it.
the insertion operation checks whether each inserted element has a key equivalent to the one of an element already in the container, and if so, the element is not inserted, returning an iterator to this existing element
if ( !myMap.insert( std::make_pair( key, value ) ).second )
{
// Element already present...
}
Are there any methods of procuring an iterator, when working with a Standard Library map container, which don't require searching throughout the container?
I have a managing class for a map, and I wish to return the iterator associated to items added to the map. I don't want to rely upon find() if at all possible. If I can avoid searches I figure all the better.
std::map<char, bool>::iterator ClassA::Add(char item)
{
mymap[item] = false;
return mymap.get_iterator_lastitem();
}
Perhaps
return mymap.end() - 1;
If you're not using C++11, then
std::map<char, bool>::iterator ClassA::Add(char item)
{
std::pair<std::map<char, bool>::iterator, bool> result = mymap.insert(std::make_pair(item, false));
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
If you are using C++11 then it is better to use emplace + auto.
std::map<char, bool>::iterator ClassA::Add(char item)
{
auto result = mymap.emplace(item, false);
if(!result.second) {
// Item already exists, modify that existing item
result.first->second = false;
}
return result.first;
}
Live example
Both insert and emplace return a pair of an iterator and a boolean, of which the iterator points to the inserted or existing element and the boolean indicates whether an insertion (true) took place or if not (false) of which the returned iterator points to the already-existing element with the key.
In my application I have a (unbalanced) tree datastructure. This tree is simply made of "std::list of std::lists" - node holds an arbitrary "list" of sub-nodes. Using this instead of a single list made the rest of the application a lot easier. (The program is about changing moving nodes from one tree to another tree / another part in the tree / to it's own tree).
Now an obvious task is to find a subtree inside a "tree". For non-recursive searches it is simple enough:
subtree_iterator find_subtree(const N& n) {
auto iter(subtrees.begin());
auto e(subtrees.end());
while (iter != e) {
if ((*iter)->name == n) {
return iter;
}
++iter;
}
return e;
}
Which returns an iterator to the subtree position. The problem however starts when I try to implement a multi-level search. Ie, I wish to search for hello.world.test where the dots mark a new level.
Searching worked alright
subtree_iterator find_subtree(const pTree_type& otree, std::string identify) const {
pTree_type tree(otree);
boost::char_separator<char> sep(".");
boost::tokenizer<boost::char_separator<char> > tokens(identify, sep);
auto token_iter(tokens.begin());
auto token_end(tokens.end());
subtree_iterator subtree_iter;
for (auto token_iter(tokens.begin()); token_iter != token_end; ++token_iter) {
std::string subtree_string(*token_iter);
subtree_iter = tree->find_subtree_if(subtree_string);
if (subtree_iter == tree->subtree_end()) {
return otree->subtree_end()
} else {
tree = *subtree_iter;
}
}
return subtree_iter;
}
On first glace it seemed to work "correct", however when I try to use it, it fails. Using it would be like
auto tIn(find_subtree(ProjectTree, "hello.world.test"));
if (tIn != ProjectTree->subtree_end()) {
//rest
}
however that gives a debug assertion error "list iterators not compatible". This isn't too weird: I'm comparing a iterators from different lists to each other. However I could I implement such a thing? My "backup" option would be to return a std::pair<bool,iterator> where the boolean part determines if the tree actually exists. Is there another method, short of making the whole tree single list?
You should not work on iterators internaly. Use nodes instead.
template <typename T>
struct Node {
T item;
Node<T>* next;
};
Then encapsulate your Node in an iterator facade like this :
template<typename T>
class iterator {
private:
Node<T>* node;
public:
...
};
Then use a generic invalid node (when node is nullptr) that is returned whenever end() is reached or returned.
Note that what i suggest is a single linked list (not double linked list as the standard one). this is because you can't go back from an invalid generic end() iterator that point to an invalid null node.
If you don't use iterator operator--() in your algorithms this should be fine.
std::vector<list_iterator> stack to traverse? Where the .back() of the stack is the only one allowed to be equal to end() of the previous one, and .front() is an iterator to the root list?