Erase keys that contain a single value in the C++ map - c++

I have the following map:
void groupIntoClasses (vector<FileData>fd, map<int,vector<FileData>> &classes )
{
classes.clear();
for (int i=0; i<fd.size(); i++)
{
string name = fd[i].fileName;
string path = fd[i].filePath;
string hash = fd[i].fileHash;
int size = fd[i].fileSize;
classes[size].push_back( {name,path,size,hash});
}
}
which matches each of my FileData objects according to the field fileSize.
What I want to do now is erase the keys which are associated with only a single value (leave only all the duplicates in the map), but I am having difficulties handling the iterator.

If I have understood you then you can write the following loop to do the task.
for ( auto it = classes.cbegin(); it != classes.cend(); )
{
if ( (*it).second.size() == 1 ) it = classes.erase( it );
else ++it;
}

Maybe something like this:
void eraseSingleEntries(map<int,vector<FileData> > &classes)
{
map<int,vector<FileData> >::iterator i = classes.begin();
while (i!=classes.end()) {
if (i->second.size()==1) {
classes.erase(i++);
}
else {
++i;
}
}
}

Related

Modification to STL List Contents in C++

In the below Code-snippet, I am trying to manipulate the contents of each of the lists present in the MAP mp but by returning a pointer to list corresponding map's key whose list needs modification. I am aware that a direct modification of the list contents is possible instead of calling getlist and then modifiying it, but I am new to STL and C++ and trying to learn STL by playing around a bit with Iterators and Lists.
When the below code is executed, a Segmentation fault is thrown at the line "(*lit) = 10". Can anyone help me understand what's going wrong here?
static void getlist(int num,map<int,list<int>> mp, list<int>** l_ptr )
{
map<int,list<int>>::iterator it = mp.begin();
while( it != mp.end())
{
if(it->first == num )
{
*l_ptr = &(it->second);
return;
}
it++;
}
}
int main()
{
map<int,list<int>> mp;
mp[1] = {2,2,2};
mp[2] = {3,3,3};
mp[3] = {4,4,4};
map<int,list<int>>::iterator it = mp.begin();
list<int>::iterator lit;
list<int>* r_l = new list<int>;
//getlist(it->first,mp,r_l);
while( it != mp.end())
{
getlist(it->first,mp,&r_l);
lit = r_l->begin();
while(lit != r_l->end())
{
(*lit) = 10;
lit++;
}
it++;
}
it = mp.begin();
while( it != mp.end())
{
lit = (it->second).begin();
while(lit != (it->second).end())
{
cout<<(*lit);
lit++;
}
it++;
}
return 0;
}

How to delete an object from a map which contains a vector as value in C++

I have a map which contains a of vector of type Messages.
std::map<std::string, std::vector<Message>> storage;
class Message has 3 member variables.
class Message
{
private:
std::string msg;
std::string msg_type;
int priority;
}
Now i am trying to delete an object which has priority(say 3) from the map. i am using the following function for it. But it doesn't work.
void deleteByMessagePriority(int priority)
{
if (checkPriorityOfMessage(priority))
{
for (std::map<std::string, std::vector<Message>>::iterator it = storage.begin(); it != storage.end(); it++)
{
std::vector<Message> listOfMsgs = it->second;
for (std::vector<Message>::iterator vec_it = listOfMsgs.begin(); vec_it != listOfMsgs.end(); vec_it++)
//for(int index = 0;index < listOfMsgs.size();index++)
{
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
}
}
}
}
Look carefully at this:
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
}
You're looking at the priority of one message (the one referred to by vec_it), but then what are you deleting if it matches?
Instead of writing your own loop, I'd use erase and std::remove_if to remove all the items you care about in that vector at once.
for (auto & item : storage) {
auto &vec = item.second;
auto start_junk = std::remove_if(
vec.begin(), vec.end(),
[=](Message const &m) { return m.priority == priority; });
vec.erase(start_junk, vec.end());
}
if (vec_it->getPriority() == priority)
{
listOfMsgs.pop_back();
pop_back() removes the last element of the vector which you don't want.You want to check erase
Also remember erase() invalidates the iterators so you need iterator to the next element after a deleted element for which we can fortunately use return value of erase
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it); //returns iterator to the element after vec_it which can also be `listOfMsgs.end()`
std::vector<Message> listOfMsgs = it->second;
.
.
.
listOfMsgs.pop_back();
You're copying the list, only to modify the copy. What you meant is:
std::vector<Message>& listOfMsgs = it->second;
Then you can proceed erasing elements. As Gaurav Sehgal says, use the return value of erase:
std::vector<Message>::iterator vec_it = listOfMsgs.begin();
while (vec_it != listOfMsgs.end())
{
if (vec_it->getPriority() == priority)
{
vec_it = listOfMsgs.erase(vec_it);
}
else
{
++vec_it;
}
}

rewrite access to collection to avoid "double" finding

I have such code:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.find(orderId);
if (i == futOrders.end()) {
LimitOrder& newOrder = futOrders[orderId];
// work
} else {
LimitOrder& futOrder = i->second;
// another work
}
Here I execute "find" twice:
first time: auto i = futOrders.find(orderId);
second time: LimitOrder& newOrder = futOrders[orderId];
Can i rewrite it somehow to avoid "double find"?
You can perform an emplace, and check the return value to know whether the item was inserted or not:
std::unordered_map<int64_t /*id_ord*/, LimitOrder> futOrders;
auto i = futOrders.emplace(
std::piecewise_construct, std::tie(orderId), std::make_tuple());
if (i.second) {
LimitOrder& newOrder = i.first->second;
// work
} else {
LimitOrder& futOrder = i.first->second;
// another work
}
How about using size() to realize if an element was inserted, like this:
auto old_size = futOrders.size();
LimitOrder& order = futOrders[orderId];
if (old_size < futOrders.size()) {
LimitOrder& newOrder = order;
// work
} else {
LimitOrder& futOrder = order;
// another work
}
Assuming there is a way to "determine if an order is empty", you could do:
LimitOrder& anOrder = futOrders[orderId];
if (anOrder.empty())
{
// New order, do stuff that only new orders need.
}
else
{
// Old order, update it.
}
The empty method could of course be something like if (anOrder.name == "") or if (anOrder.orderId == 0), etc.
You can use this overload of insert instead:
std::pair<iterator,bool> insert( const value_type& value );
Example:
std::unordered_map<int, std::string> m { {0, "A"}, {1, "B"}, {2, "C"} };
int orderId = 1;
// attempt to insert with key you have and default constructed value type
auto p = m.insert( std::make_pair(orderId, std::string()) );
if (p.second) {
// the element was inserted
} else {
// the element was not inserted
std::cout << p.first->second; // will print "B"
}
In both cases, p.first is the iterator to the element you search for (or just got inserted).

Insert into a vector in alphabetical order

I am trying to insert into this Vector (mCards) in alphabetical order. I can't use sort - it must be inserted in alphabetical order! Right now, I'm inserting in reverse. I can't figure out how to get this to insert in correct alphabetical order!
void Rolodex::Add(Card& card)
{
vector<Card>::iterator temp;
if (mCards.size() != 0)
{
for(vector<Card>::iterator it = mCards.begin(); it != mCards.end(); ++it)
{
Card& currentCard = *it;
temp = it;
int compareResult = currentCard.GetLastName().compare(card.GetLastName());
if (compareResult <= 0)
{
mIteratorNumber = it - mCards.begin();
mCards.insert(temp, card);
return;
}
}
}
else
{
mCards.push_back(card);
for(vector<Card>::iterator it = mCards.begin(); it != mCards.end(); ++it)
{
mIteratorNumber = it - mCards.begin();
}
}
}
If you wants a sorted container, you may instead look at std::map and std::set or their multi variant if you may have duplicated values.
To insert into a sorted vector, the right way to do it is to use std::upper_bound
myCards.insert( std::upper_bound( begin(myCards), end(myCards), card), card );
If the card do not have a valid operator<, use a predicate like this
auto it = std::upper_bound( begin(myCards), end(myCards), card,
[] ( Card const & a, Card const & b ) {
return a.GetLastName().compare(b.GetLastName()) < 0;
} );
myCards.insert( it, card );

How to convert this C# script to native C++?

I'm beginner in C++. Before I work with C#. Bellow is a C# script. How I can do same these things in native C++?
All I need is:
A list, or similar, have int-int key-value pair
Can auto sort by value. If not, it must be sortable by key and it can get index of a
value (each of my values is definite)
I tried std::map but it don't have built-in sort by value or get key by value. Is C++ have similar thing like sortedlist in c#?
Thank you very much!
public static SortedList<int, int> sortedList1 = new SortedList<int, int>();
static void List_Add(int i) // 0 < i < 1000
{
if (!sortedList1.ContainsValue(i))
sortedList1[Environment.TickCount] = i;
}
static void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.ContainsValue(i))
sortedList1.RemoveAt(sortedList1.IndexOfValue(i));
}
static int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
foreach (KeyValuePair<int, int> i in sortedList1)
{
if (i.Value > 90) modifier = true;
if (i.Key - time > 200 | modifier | keys > 1000)
{
keys = keys * 1000 + i.Value;
time = i.Key;
}
}
return keys;
}
You seem to be doing the wrong way round as usually things are sorted using keys and queries are done using the key not using the value. However, it seems a std::map<int,int> will help you here. Simply use your value as a key of the map and your key as value(so that you can query using the value). Use multimap if duplicates are allowed.
This are some converter tools:Please visit the following links:
http://sourceforge.net/projects/convetercpptocs/
http://www.tangiblesoftwaresolutions.com/Product_Details/CSharp_to_CPlusPlus_Converter_Details.html
http://cscpp.codeplex.com/
Like that:
#include <map>
#include "Winbase.h"
std::map<int, int> sortedList1;
void List_Add(int i) // 0 < i < 1000
{
if (sortedList1.find(i) == sortedList1.end())
sortedList1.insert(std::make_pair<int, int>(GetTickCount(), i));
}
void List_Remove(int i) // 0 < i < 1000
{
if (sortedList1.find(i) != sortedList1.end())
sortedList1.erase(sortedList1.find(i));
}
int List_toInt()
{
int time = 0;
int keys = 0;
bool modifier = false;
for (std::map<int, int>::const_iterator it = sortedList1.cbegin();
it != sortedList1.cend(); it++)
{
if (it->second > 90) modifier = true;
if (it->first - time > 200 || modifier || keys > 1000)
{
keys = keys * 1000 + it->second;
time = it->first;
}
}
return keys;
}