I want to remove selected rows from a treeview or the underlying model.
The following code snipped works, but I have no idea which function of which class I have to call to remove the selected elements.
std::vector<Gtk::TreeModel::Path> pathlist;
pathlist = get_selection()->get_selected_rows();
for ( std::vector<Gtk::TreeModel::Path>::iterator it = pathlist.begin(); it!=pathlist.end(); it++)
{
Gtk::TreeModel::iterator iter = get_model()->get_iter( *it );
Gtk::TreeModel::Row row = *iter;
int val;
std::string str;
row.get_value( 0, val );
row.get_value( 1, str );
std::cout << "val " << val << std::endl;
std::cout << "String:" << str << std::endl;
}
The above code works fine.
Now I want to delete the elements which are selected!
Attention: MULTIPLE selection is activated.
I understand that the main problem is MULTIPLE selection - if you get only one item then it's simple:
get_model()->erase(iter);
The problem is after that; the rest of iterators become invalid.
Do you have any unique ID for each row? If YES, then you can use that:
Store all IDs in container
go throu all items and delete the stored
something like that:
std::vector<Gtk::TreeModel::Path> pathlist;
pathlist = get_selection()->get_selected_rows();
std::set<int> IDs;
for ( std::vector<Gtk::TreeModel::Path>::iterator it = pathlist.begin(); it!=pathlist.end(); it++)
{
Gtk::TreeModel::iterator iter = get_model()->get_iter( *it );
iter->get_value(ID, id);
IDs.insert(id);
}
auto iter = get_model()->erase( get_model()->get_iter( *pathlist.begin() );
while (iter)
{
int id;
iter->get_value(ID, id);
if (IDs.find(id) != IDs.end()) {
iter = get_model()->erase( iter );
else
++iter;
}
Or something like that. Sorry, I don't remember whole API.
I assume that erasing row from model changes also PATH.
Related
I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.
I have a map defined as std::map<std::string, textInfo> tempMap;
the textInfo class has some attributes as textsize,textcolor,textfont etc..
I want to select an item from this map that matches with a given value to an attribute in textInfo class.
for example if the Map contains
<"A",textInfo("10","Red","Verdana")>
<"B",textInfo("12","Green","Timesnewroman")>
<"C",textInfo("11","Blue","Cambria")>
I want to select the item that contains "Cambria" in it textfont attribute.
<"C",textInfo("11","Blue","Cambria")>
std::find_if should work for your needs.
Sample program:
#include <iostream>
#include <map>
#include <algorithm>
struct textInfo
{
std::string textsize;
std::string textcolor;
std::string textfont;
};
int main()
{
std::map<std::string, textInfo> m =
{
{"A", {"10","Red","Verdana"}},
{"B", {"12","Green","Timesnewroman"}},
{"C", {"11","Blue","Cambria"}}
};
auto iter = std::find_if(m.begin(),
m.end(),
[](std::pair<std::string, textInfo> const& item)
{ return (item.second.textfont == "Cambria");});
if ( iter != m.end() )
{
auto& item = iter->second;
std::cout << item.textsize << ", " << item.textcolor << ", " << item.textfont << std::endl;
}
}
Output:
11, Blue, Cambria
You can only access maps directly via a key, here your std::string. To search for a value or even a variable inside a value like it's the case here you have to iterate over the whole map.
std::map<std::string, textInfo>::const_iterator it = tempMap.begin();
for (; it != tempMap.end(); ++it)
{
if (0 == tempMap[(*it)].textfont.equals("Cambria")) // You could use == operator if it's a std::string
{
break; // found
}
}
// Do something with the found item. If the iterator is tempMap.end(), nothing found!
Look here for more informations.
list<CPoint> l;
l.push_back( CPoint(1,2) );
l.push_back( CPoint(30,40) );
l.push_back( CPoint(4,6) );
l.push_back( CPoint(70,80) );
CPoint * point = 0;
for ( list<CPoint>::iterator iter = l.begin();
iter != l.end();
iter++)
{
cout << iter->x << " , " << iter->y << endl;
// compilation error, I can't typcast it like below?
point = (CPoint *) iter;
}
The problem with above is that how to typcast the iter in the loop to the actual data structure pointer? That way I can write code like point.x, point.yto say the least.
The above is the demo code I wrote but in reality I have this code in a search function. If an item is found in the list, it will return pointer to that item otherwise NULL. In order to get that pointer back I need to dereference the iterator back to the underlying data structure pointer but how? Thanks.
To fix your syntax error, you need to dereference iterator then take the address from the underneath object:
point = &*iter;
You'd better just use std::find/std::find_if and store the iterator which is returned from std::list.
auto it = std::find_if(l.begin(), l.end(),
[](const CPoint& cp) { return cp.x == 1 && cp.y == 2; } );
if (it != l.end()) // test iterator to see is desired CPoint is found
{
std::cout << (*it).x << " " << (*it).y << std::endl;
}
Given a map, I need to retrieve and operate two immediately stored items.
To me, working on a vector is litter easier since I can do "iter + 1" or "iter - 1".
While for map, I am out of luck.
For example, I give a simple example as follows:
Note: in my real application, I don't simply subtract those numbers.
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
// show content:
map<char,int>::iterator firstItem = mymap.begin();
map<char,int>::iterator secondItem = ++mymap.begin();
for ( ; secondItem != mymap.end(); ++firstItem, ++secondItem )
cout << secondItem->second - firstItem->second << endl;
return 0;
}
Question> Is there a better solution for this?
Thank you
Instead of incrementing both iterators in the loop control (incrementing is a bit slow), just assign firstItem = secondItem then increment secondItem.
You can do it with a single iterator. Move the increment from the header to the middle of your loop, and exit the loop when you hit the end of your map, like this:
map<char,int>::iterator item = mymap.begin();
for (;;) {
int first = item->second;
++item;
if ( item == mymap.end()) break;
cout << item->second - first << endl;
}
This is a matter of style. You can do eg.
auto first = m.begin();
if (first != m.end())
{
auto second = first;
second++;
for (; second != m.end(); first = second++)
{
...
}
}
You can also bailout more elegantly in the case where the map is empty. For instance you can do:
if (m.empty()) return;
auto first = m.begin(), second = first;
for (second++; second != m.end(); first = second++)
{
...
}
I'd favor the latter if I can, and use the former only if I must.
Your current loop will show undefined behaviour if the map is empty.
Your loop could be rewritten (more simply, and checking for an empty map) like so:
int main(int argc, char * argv[])
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['b'] = 100;
mymap['a'] = 200;
mymap['c'] = 300;
for ( it = ( mymap.begin() == mymap.end() ? mymap.end() : std::next(mymap.begin()) ) ; it != mymap.end(); ++it )
cout << it->second - std::prev(it)->second << endl;
return 0;
}
Your code will have undefined behavior if the map is empty but other than that it seems to be a reasonable approach, depending on your overall goal. Since map iterators are not random access you can't just add or subtract one, only increment/decrement.
An alternate approach is to make a copy of the iterator and then incrementing inside the loop.
Neither better, nor worse, just an alternative:
if (map.size() >=2)
std::accumulate(
++mymap.begin(),
mymap.end(),
mymap.begin(),
[](mymap_type::const_iterator iprev, mymap_type::value_type const& entry)->mymap_type::const_iterator
{
/* do something */;
return ++iprev;
});
If i iterate over a STL container i sometimes need to know if the current item is the last one in the sequence. Is there a better way, then doing something like this? Can i somehow convert rbegin()?
std::vector<int> myList;
// ....
std::vector<int>::iterator lastit = myList.end();
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != myList.end(); it++) {
if(it == lastit)
{
// Do something with last element
}
else
{
// Do something with all other elements
}
Try the following
std::vector<int>::iterator it2 = (++it);
if ( it2 == myList.end() ) {
...
}
The following should work as well
if ( it+1 == myList.end() ) {
// it is last
...
}
Maybe you can iterate backwards (use rbegin/rend) and put your special task before the loop or replace the end check with it != lastit and put the special handling after the loop
I would have some doubts about my design if some elements need to be treated differntly, but this suggestion is a bit cleaner for me (don't forget to test for empty containers)
std::vector<int>::iterator lastit = myList.end();
if (lastit != myList.begin())
{
lastit--;
for(std::vector<int>::iterator it = myList.begin(); it != lastit; ++it)
{
// Do
}
// Do with last
}
Use reversed iteration, this way you will have only one end()-1-like computation (notice the rbegin()+1) and no comparsions:
for(vector<int>::iterator it = myValues.rbegin()+1; it != myValues.rend(); it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
Also, for the vector<>, computing end()-1 is probably fast, so you can also do it like following:
for(vector<int>::iterator it = myValues.begin(); it != myValues.end()-1; it++) {
cout << *it << endl;
}
cout << "Process last one: " << *myValues.rbegin() << endl;
If you don't want to process the element after the loop, you can:
for(vector<int>::iterator it = myValues.rbegin(); it != myValues.rend(); it++) {
if(it == myValues.rbegin())
cout << "Process last one: " << *it << endl;
else
cout << *it << endl;
}
For a random access iterator like that for vector, you don't need the temporarary. You can say:
if ( it + 1 == v.end() ) {
// at one before end
}
Edit: And even for non-random access types one could use std:;distance:
if ( distance( it, v.end() ) == 1 ) {
// at one before end
}
An important question is: why create a loop if you do something special for 1 element. Why not do something special to the 3rd element? To every 4rth? ...
Just iterate over the elements to be treated the same, write separate code to treat the others.
Have a look at answers to this question, too.
Why not:
if(!myList.empty())
last_it = myList.begin() + myList.size()-1;
else
last_it = myList.end();
//or
last_it = myList.empty() ? myList.end() : myList.begin() + myList.size() - 1;
If you're using a vector, it's actually much simpler to use an integer index to iterate:
std::vector<int> myList;
for (unsigned int i = 0; i < myList.size(); i++)
{
if (i == (myList.size() - 1))
{
processDifferently (myList[i])
}
else
{
process (myList[i])
}
}
Minimizing the number of calls to myList.size() is left as an exercise for the OP :)