I have two classes: InitTable and InitEntry. InitTable contains a std::map table
which stores the ID
of an entry (car), and on object ( InitEntry
) which represents that car. Every InitEntry
has 3 member variables:
std::string ID
double speed
std::string heading
My goal is to first store all the cars in the std::map table
, and then iterate through that data structure, and try to organize the cars into clusters ( std::vector<InitEntry>
) based on common properties: speed
& heading
.
Example:
Lets assume we have 6 cars (ids 0 to 8)
To keep it simple, at this stage for me it would be enough to group the cars only based on heading. And the result would be:
std::vector clus1 = {car0, car1}
std::vector clus2 = {car2}
std::vector clus3 = {car3, car4}
std::vector clus4 = {car5}
Unfortunately, I do not have enough knowledge of C++ STL to be able to understand how to achieve this in C++.
InitTable.h:
#include <InitEntry.h>
class InitTable {
public:
InitTable();
virtual ~InitTable();
void clearTable();
void addEntry(std::string ID, double speed, std::string heading);
void deleteEntry(std::string ID);
InitEntry* getEntry(std::string ID);
protected:
std::map<std::string, InitEntry*> table;
};
InitTable.cc:
#include"InitTable.h"
InitTable::InitTable(){}
InitTable::~InitTable()
{
clearTable();
}
void InitTable::clearTable()
{
this->table.clear();
}
void InitTable::addEntry(std::string ID, double speed, std::string heading)
{
InitEntry* newEntry = new InitEntry(ID, speed, heading);
std::cout<< "server::InitTable: vehicle registered to the init table" << newEntry << endl;
table.insert(std::make_pair(ID, newEntry));
}
void InitTable::deleteEntry(std::string ID)
{
InitEntry* ie = getEntry(ID);
if (ie != NULL)
{
table.erase(ID);
delete ie;
}
}
InitEntry* InitTable::getEntry(std::string ID)
{
std::map<std::string, InitEntry*>::iterator it = table.find(ID);
if (it != table.end())
{
return it->second;
}
else
{
std::cout << "such entry does not exist" << endl;
return NULL;
}
}
InitEntry.h:
class InitEntry {
public:
virtual ~InitEntry();
InitEntry(std::string ID, double speed, std::string heading);
std::string getID();
protected:
std::string sumoID;
double speed;
std::string heading;
};
InitEntry.cc:
#include "InitEntry.h"
InitEntry::InitEntry(std::string ID, double speed, std::string heading): ID(ID), speed(speed), heading(heading){}
InitEntry::~InitEntry(){}
std::string InitEntry::getID()
{
return this->ID;
}
EDIT 1: adding extra description (by request of @TomaszLewowski).
Yes, my goal would be to organize the vehicles in clusters, by the heading, and then based on the speed. So initially there would be one big cluster of vehicles going on a certain direction, which later would need to be split into more clusters, based on speed. Lets say: vehicles heading "north", with speed: 0 - 20... with speed speed: 40 - 50...etc
Consider changing your std::map<std::string, InitEntry*> table;
to:
std::vector<InitEntry> table;
You would then need to change your *Entry
methods to:
void InitTable::addEntry(std::string ID, double speed, std::string heading)
{
table.push_back(InitEntry(ID, speed, heading));
std::cout<< "server::InitTable: vehicle registered to the init table" << table.back() << endl;
}
void InitTable::deleteEntry(std::string ID)
{
auto it = remove_if(table.begin(), table.end(), [&](const auto& i){return i.getID() == ID;});
table.resize(distance(table.begin(), it));
}
InitEntry* InitTable::getEntry(std::string ID)
{
auto it = find_if(table.begin(), table.end(), [&](const auto& i){return i.getID() == ID;});
if (it != table.end())
{
return it->second;
}
else
{
std::cout << "such entry does not exist" << endl;
return NULL;
}
}
To sort table
you would need to decide what to sort by.
std::sort(table.begin(), table.end(), [](const auto& first, const auto& second){return first.getID() < second.getID();});
std::sort(table.begin(), table.end(), [](const auto& first, const auto& second){return first.getSpeed() < second.getSpeed();});
std::sort(table.begin(), table.end(), [](const auto& first, const auto& second){return first.getHeading() < second.getHeading();});
Obviously you'd need to add the getters to make these work.
If you don't want to iterate over all elements (and I guess you don't), then only option is to keep these groups somewhere, and only some indices to them in the main table. This would look roughly like that:
#include <string>
#include <vector>
#include <map>
#include <cassert>
#include <algorithm>
typedef std::string ID;
typedef std::string Heading;
typedef double Speed;
struct Entry
{
ID id;
Speed speed;
Heading heading;
};
class EntryProxy
{
public:
EntryProxy(Entry* target) : entry(target)
{}
ID getId()
{
return entry->id;
}
Speed getSpeed()
{
return entry->speed;
}
Heading getHeading()
{
return entry->heading;
}
private:
Entry* entry;
};
class InitTable
{
public:
const std::vector<EntryProxy>& getSameHeading(std::string id)
{
return groupedByHeadings.at(entries.at(id).heading);
}
const std::vector<EntryProxy>& getSimilarSpeed(std::string id)
{
return groupedBySpeed.at(calculateSpeedGroupIndex(entries.at(id).speed));
}
void addEntry(ID id, Speed speed, Heading heading)
{
Entry e{ id, speed, heading };
auto record = entries.insert(std::make_pair(id, e)); // pair<iterator, bool>
groupedByHeadings[record.first->second.heading].push_back(EntryProxy(&record.first->second));
groupedBySpeed[calculateSpeedGroupIndex(record.first->second.speed)].push_back(EntryProxy(&record.first->second));
}
void deleteEntry(ID id)
{
auto entry = entries.find(id);
assert(entry != entries.end());
auto currentEntryHeadings = groupedByHeadings[entry->second.heading];
currentEntryHeadings.erase(std::find_if(currentEntryHeadings.begin(), currentEntryHeadings.end(), [&entry](EntryProxy p){return p.getId() == entry->second.id; }));
auto currentEntrySpeeds = groupedBySpeed[calculateSpeedGroupIndex(entry->second.speed)];
currentEntrySpeeds.erase(std::find_if(currentEntrySpeeds.begin(), currentEntrySpeeds.end(), [&entry](EntryProxy p){return p.getId() == entry->second.id; }));
entries.erase(id);
}
EntryProxy getEntry(ID id)
{
return EntryProxy(&entries.at(id));
}
private:
typedef int SpeedGroupIndex;
SpeedGroupIndex calculateSpeedGroupIndex(Speed s)
{
// you may prefer a different implementation here
return s / 10;
}
std::map<ID, Entry> entries;
std::map<Heading, std::vector<EntryProxy> > groupedByHeadings;
std::map<SpeedGroupIndex, std::vector<EntryProxy> > groupedBySpeed;
};
Grouping by Heading is pretty obvious, as it requires only a simple map. However, when it comes to speed it's a little tougher - you need to describe requirements for grouping (by implementing function getSpeedGroupId
), as doubles shouldn't be used as map indices.
In case you prefer a more safe approach towards memory management you can substitute Entry*
for std::shared_ptr<Entry>
in EntryProxy
and Entry
for std::shared_ptr<Entry>
in InitTable::entries
. In that case you would also need to change creation from Entry e{id, speed, heading
to auto e = std::make_shared<Entry>(id, speed, heading)
.
If deleting is too complex for you, you may keep EntryProxy
structures in a list, and a reference to specific iterators in entries
map.
You may also not like the fact that groups are EntryProxy
and single elements are Entry
- in such case you may make getEntry
also return EntryProxy
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.