Deleting a "value" from a multimap - c++

My requirement is to delete a a "value" from the multimap and not the "key".
A key may have multiple values and i want delete a specific value.My requirement is similar to deleting a node from a linked list.
I am doing so by using multimap::erase() method.
But after deletion if I try to print the values of the multimap, the values deleted using multimap::erase() are also printed.
below is my code snippet:
void Clientqueues::clearSubscription(string name,string sessionid)
{
pair<multimap<string,string>::iterator,multimap<string,string>::iterator> i;
multimap<string, string>::iterator j;
i = registeredClientInfo.equal_range(name);
if (j == registeredClientInfo.end())
return;
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) registeredClientInfo.erase(j->second);
}
for(j=i.first;j != i.second;++j)
{
cout<<""<<j->second<<endl;///This prints the erased values too;
}
}
Am i doing something wrong?
Any help in this regard greatly appreciated.

Most important, you call erase(j->second), when you meant to call erase(j). You're not erasing the element of the multimap pointed to by j, you're erasing all elements whose keys are equal to the value of the element pointed to by j (which is sessionid). I expect that's nothing.
Also: call equal_range again after the erase loop is complete - the effect of using an erased iterator is undefined, so if you erased the first iterator i.first, then you can't start iterating from there again afterwards.
Note that this also means there's a bug in your loop that does the erase, since in the case that you do call erase, you increment j when it holds an iterator value that's no longer valid. Unfortunately, the correct code is:
for(j=i.first;j != i.second;)
{
if((j->second) == sessionid) {
auto next = j;
++next;
registeredClientInfo.erase(j);
j = next;
} else {
++j;
}
}
Or if you prefer:
for(j=i.first;j != i.second;)
{
auto current = j;
++j;
if((current->second) == sessionid) registeredClientInfo.erase(current);
}
Or if the entry is unique for the key/value pair, so that you only have to remove at most one thing, then:
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) {
registeredClientInfo.erase(j);
break;
}
}
if (j == registeredClientInfo.end()) return; isn't right either, since j is uninitialized when you do it. If the key isn't found, then equal_range returns an empty range (two equal iterator values), so your other loops will do nothing anyway.

If you deleted i.first or i.second the iterators get invalidated implying undefined behavior.

Related

Can't pass all test cases for LeetCode question to find intersection of two linked lists using Hash map

I passed the initial test case but it is showing wrong answer for a test case.( Linked lists given in test case : [0,9,1,2,4] and [3,2,4]. The expected answer is node with value 2 but i am getting 4.) Can anyone spot error in the code?
Other ways of doing the problem are welcomed but i wanted to do this with Hash map for my own practice.
class Solution{
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_map<int, ListNode*> map;
int i=0;
while(headA!=NULL){ //inserting all nodes of LL A in hash map
map[i]=headA;
headA=headA->next;
i++;
}
while(1){
for (auto it = map.begin(); it != map.end(); it++) {
if (it->second == headB){
return it->second;
}
else{
if(headB->next!=NULL) {
headB=headB->next;
}
else{
break;
}
}
}
}
return NULL;
}
};
I'd like to preface this by saying that an unordered_map has no guarantees of storage order. This means that items aren't necessarily stored in the same order as they are inserted.
Let's look at the relevant code.
while(1){
for (auto it = map.begin(); it != map.end(); it++) {
if (it->second == headB){
return it->second;
}
else{
if(headB->next!=NULL) {
headB=headB->next;
}
else{
break;
}
}
}
The iterator is going to loop through the map's stored order in an order you essentially cannot predict. From a cursory glance of your code, I can see that your program is going to return in one of the following two cases:
If the random order the iterator goes through the map happens to have
a value that lines up with a corresponding headB value.
If headB reaches it's last value without finding a match, the while(1) would restart the inner for loop and would compare the first value pointed at by the iterator to the last value in the headB linked list. If that comparison succeeds, then the program would return. Otherwise, the loop goes on forever.

Delete Elements from Vector Inside Loop

I have a vector, words_in_family, of type: vector<vector<string>>. I am trying to delete every element of words_in_family that is not equal to the string vector largest_family, but am having issues and am unsure of why. Any help is appreciated.
for (int i = words_in_family.size() - 1; i >= 0; i--)
{
if (words_in_family[i] != largest_family)
{
words_in_family.erase(words_in_family[i]);
}
}
erase method doesn't take value. Instead use iterator as parameter for erase method:
vector<vector<string>>::iterator it = words_in_family.begin();
for (int i = words_in_family.size() - 1; i >= 0; i--)
{
if (words_in_family[i] != largest_family)
{
words_in_family.erase(it+i);
}
}
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
Note that the iterators for std::vector are random access iterators so you can add/subtract integral values to get other valid iterators.
C++ std::vector erase an element, in your case:
Erase an element from a vector(words_in_family) whose value is equal to largest_family:
std::vector<std::vector<std::string>>::iterator Itr;
for(Itr = Words_in_family.begin(); Itr != Words_in_family.end();)
{
if(*Itr == largest_family)
{
Itr = Words_in_family.erase(Itr);
}
else
{
Itr++;
}
}
words_in_family.erase(words_in_family.begin()+i, words_in_family.begin()+i+1);
erase takes iterator as an argument

Removing from std::list while iterating

I have the following code:
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ++it) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
}
So I have the following scenario: eventList contains 01 item, and then, as soon as the for statement goes through for the first time and meet the it = eventsList.erase(it); line, the it variable becomes invalid, causing a segmentation fault on the next iteration of the for statement.
Any clues of what could be causing the problem?
If the item you remove is the last item in the list, the erase method will return end(). Your for loop will then try to increment that iterator, which results in undefined behaviour.
Another problem which you haven't come across yet is that, if the item you remove isn't the last item in the list, you'll end up skipping over the following item (because the iterator is incremented past the one that erase returns). You can think of erase as an increment operation that just happens to erase the item first.
The solution is to refactor the loop slightly, to move the increment to the end (and only if erase wasn't called):
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
}
else {
if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it; // move the increment to here
}
}
As it is written now, you are incrementing the iterator even in the erase branch, which means that you are always skipping the element just after an erased one. This is both incorrect and results in serious problems if the last element happens to be one to delete. To fix, you have to not increment if you already fix it by setting it to the element following the deleted one.
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
continue;
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it;
}

How to remove odd positions from a list?

#include<iostream>
#include<list>
using namespace std;
void compute(int num)
{
list<int> L;
list<int>::iterator i;
list<int>::iterator i2;
int p;
cout<<"Enter the number of numbers\n";
cin>>p;
int a;
for(int k=1;k<=p;k++)
{
cin>>a;
L.push_back(k);
}
cout<<endl;
for(i=L.begin() ; i!=L.end() ; ++i)
{
cout<<*i<<endl;
}
long int k=1;
for(i=L.begin() ; i!=L.end() ; ++i )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i2=L.erase(i);
}
k++;
}
for(i=L.begin() ; i!=L.end() ; ++i )
{
cout<<*i<<endl;
}
}
int main()
{
// int testcases, sailors;
//cin>>testcases;
//for(int i=1 ; i<=testcases ; i++)
{
// cin>>sailors;
}
//for(int i=1;i<=testcases;i++)
{
// int num;
//cin>>num;
//compute(num);
}
compute(0);
return 0;
}
I am trying to erase elements using L.erase() function in Lists. But I get an error saying
"Debug assertion failed! ......Expression:list iterator not incrementable"
but we CAN increment iterator right?
erase invalidates the iterator that was passed in as parameter - since the element at the position the iterator was pointing to was just erased! And on that same iterator, an increment is attempted in the next for loop in your code! That's why it fails.
However, erase it will return an iterator pointing to the new position, which we can use; a loop where you erase something from an STL container should therefore look something like the following; I show it with the type you use, list, but you could just as well use e.g. vector:
list<int> L;
// ...
list<int>::iterator it=L.begin();
while (it!=L.end())
{
if(eraseCondition)
{
it=L.erase(it);
}
else
{
++it;
}
}
Or, if possible, it's even better to use std::remove_if:
container.erase(std::remove_if(L.begin(), L.end(), predicate), L.end());
In your case that will be hard - if not impossible - to use since the predicate would need state information (the information whether the index is odd or even). So I'd recommend going with a loop structure as mentioned above; just keep in mind the remove_if for the general case of removing all elements where a certain predicate returns true!
Adding to what wOOte said, you may want to used a reverse iterator to get around the issue.
Technically not in this case.
When you use erase() you delete the node that was pointed to, so you actually invalidate the iterator you were on. So when you increment it it's undefined behavior.
It might be best to create a second list with just the iterators to the positions you'd like to delete, and you can cycle through those and call erase afterward. You wouldn't be erasing the iterators from the second list, so it'd work.
Something like this:
List<IteratorType> deleteList;
//Populate deleteList with every other element from original list.
for (List<IteratorType>::iterator iter = deleteList.begin();
iter !=deleteList.end; ++iter)
{
originalList.erase(*iter);
}
The iterator i is invalidated by the call to erase; however, in the next iteration of the for loop, you try to increment it - this is invalid.
Try
for(i=L.begin() ; i!=L.end() ; )
{
if(k%2!=0) //This is where I try and delete values in odd positions
{
i=L.erase(i);
} else {
++i;
}
k++;
}
instead - only increment the iterator if you don't erase (erase basically "advances" the iterator because it yields an iterator to the element following the one you erased).
You can actually exploit this behaviour of erase to write your function without requiring k:
i = L.begin();
while ( i != L.end() ) {
i = L.erase( i ); // Delete one
if ( i != L.end() ) { // Skip next, if there's an element
++i;
}
}
So you delete the first element, skip the second, delete the third, and so on.

List Iterator Remove()

I have a list iterator that goes through a list and removes all the even numbers. I can use the list iterator to print out the numbers fine but I cannot use the list's remove() and pass in the dereferenced iterator.
I noticed that when the remove() statement is in effect, *itr gets corrupted? Can somebody explain this?
#include <iostream>
#include <list>
#define MAX 100
using namespace std;
int main()
{
list<int> listA;
list<int>::iterator itr;
//create list of 0 to 100
for(int i=0; i<=MAX; i++)
listA.push_back(i);
//remove even numbers
for(itr = listA.begin(); itr != listA.end(); ++itr)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
listA.remove(*itr); //comment this line out and it will print properly
}
}
}
There are a few issues with your code above. Firstly, the remove will invalidate any iterators that are pointing at the removed elements. You then go on to continue using the iterator. It is difficult to tell which element(s) remove would erase in the general case (although not in yours) since it can remove more than one.
Secondly, you are probably using the wrong method. Remove will iterate through all of the items in the list looking for any matching elements - this will be inefficient in your case because there is only one. It looks like you should use the erase method, you probably only want to erase the item at the position of the iterator. The good thing about erase is it returns an iterator which is at the next valid position. The idiomatic way to use it is something like this:
//remove even numbers
for(itr = listA.begin(); itr != listA.end();)
{
if ( *itr % 2 == 0 )
{
cout << *itr << endl;
itr=listA.erase(itr);
}
else
++itr;
}
Finally, you could also use remove_if to do the same as you are doing:
bool even(int i) { return i % 2 == 0; }
listA.remove_if(even);
You can't use an iterator after you delete the element it referred to.
However, list iterators which refer to non-deleted items after a remove() should remain valid.
Could we use something like this:
container.erase(it++);
I tried on this example:
int main(){
list<int>*a=new list<int>;
a->push_back(1);
a->push_back(2);
a->push_back(3);
list<int>::iterator I;
I=a->begin(); ++I;
a->erase(I++);
cout<<*I<<endl;
}
and it displayed 3, as I wanted. Now I don't know if this is valid or one of those which "sometimes work and sometimes not".
EDIT: Maybe it is because of compiler. For example, compiler I am using (GNU gcc-g++) is treating lists (std::) as circular, ie if I increase iterator after list->end() it puts you at the beginning.
Since iterators depend on the length of the structure remaining the same, most iterators do not allow a list to be changed while the iterator is in use. If you want to go through and change the list, you're going to have to use a loop independent of the iterator.