Std::map \ std::set contain duplicate keys - c++

I have an issue and though I understand, that is kind of a stupid question to ask, but I failed to find a solution on my own.
So, I'm trying to accumulate a container with unique values of a structure I have.
struct Symbol {
D2D1_RECT_F bbox;
wchar_t data;
fz_font_s* font;
float color[4];
};
What I'm doing is trying to use std::map and std::set. Being aware, that I need to provide a predicate in order to give container a way to determine the order. What I have is:
struct SymbolCmp {
bool operator() (const Symbol& lhs, const Symbol& rhs) const
{
auto errorHandler = (lhs.bbox.top == rhs.bbox.top) ? (lhs.bbox.left < rhs.bbox.left) : lhs.bbox.top < rhs.bbox.top;
if (lhs.data == rhs.data &&
lhs.font != rhs.font) {
return errorHandler;
}
float lArea = (lhs.bbox.bottom - lhs.bbox.top) *
(lhs.bbox.right - lhs.bbox.left);
float rArea = (rhs.bbox.bottom - rhs.bbox.top) *
(rhs.bbox.right - rhs.bbox.left);
auto relative = (lArea / rArea < 0.95f ||
lArea / rArea > 1.05f);
return (lhs.data == rhs.data) ? relative && errorHandler : (lhs.data < rhs.data);
}
};
And then I just try inserting values inside of std::set<Symbol, SymbolCmp> and std::map<Symbol, byte, SymbolCmp>.
Sadly enough the results are frustrating, cause what I get is pretty far from an object, containing unique keys only. Most of Symbol's have duplicates.
So I REALLY to understand, what I'm missing?

Your predicate doesn't ensure strict-weak ordering. Following should work:
struct SymbolCmp {
bool operator() (const Symbol& lhs, const Symbol& rhs) const
{
if(lhs.data == rhs.data) {
return (lhs.bbox.top == rhs.bbox.top) ? (lhs.bbox.left < rhs.bbox.left) : lhs.bbox.top < rhs.bbox.top;
} else {
return lhs.data < rhs.data;
}
}
};
If you want to use font, color and dimension in the logic, ensure that your follow the strict-weak ordering constraint.

Related

Remove what's in one list from the other list

I have two lists which contain a bunch of elements of the same type:
std::list<Part> allParts = step.getSubParts();
std::list<Part> toRemove;
for (Part part : allParts)
{
for (Part partTwo : allParts) {
if (part.getEdges() == partTwo.getEdges())
if (part.getFaces() == partTwo.getFaces())
if (part.getShells() == partTwo.getShells())
if (part.getVertices() == partTwo.getVertices())
if (part.getWires() == partTwo.getWires())
{
part.addAmount(1);
toRemove.push_back(partTwo);
}
}
}
I have tried iterating through both and remove from the one but I'm constantly getting the list iterators are incompatible error. This is my latest attempt:
std::list<Part>::iterator it;
for (it = step.getSubParts().begin(); it != step.getSubParts().end();)
{
std::list<Part>::iterator i;
for (i = toRemove.begin(); i != toRemove.end();)
{
if (it->getEdges() == i->getEdges())
if (it->getFaces() == i->getFaces())
if (it->getShells() == i->getShells())
if (it->getVertices() == i->getVertices())
if (it->getWires() == i->getWires())
{
it = step.getSubParts().erase(it);
}
else
{
it++;
}
i++;
}
}
Everything I have tried doesn't work. What is the correct way to do this?
You should consider remove_if or erase_if rather than doing your own erase with the hazard of making iterator invalid within a loop.
By the way, you should write predicate like:
if (it->getEdges() == i->getEdges() &&
it->getFaces() == i->getFaces() &&
it->getShells() == i->getShells() &&
it->getVertices() == i->getVertices() &&
it->getWires() == i->getWires()) {
// do something
}
Your code makes people difficult to understand your purpose(at least me).
erase and erase_if
First of all, it would be a good idea to follow the Don't Repeat Yourself principle and write a comparison function for future use:
auto compare_parts = [](const Part& p1, const Part& p2) -> bool {
return ( (p1.getEdges() == p2.getEdges())
and (p1.getFaces() == p2.getFaces())
and (p1.getShells() == p2.getShells())
and (p1.getVertices() == p2.getVertices())
and (p1.getWires() == p2.getWires()) );
}
You would rewrite the first cycle using it and see how much more simple it looks.
Then why not use c++ built-in methods to erase the elements from the list using the function we wrote? This uses new feature in c++ called binding parameters that would aid us here
#include <functional>
using namespace std::placeholders;
for (auto&& badPart : toRemove) {
auto isBad = std::bind(compare_parts, badPart, _1);
step.getSubParts().remove_if(isBad);
}
And that's how you remove special entries from the list.
I think the cleanest way would be:
1. Implement equality operator for the class Part
You can either put it inside or outside the class, it would look like this if you implement it as an external function
inline bool operator==(const Part& lhs, const Part& rhs) {
return lhs.getEdges() == rhs.getEdges() &&
lhs.getFaces() == rhs.getFaces() &&
lhs.getShells() == rhs.getShells() &&
lhs.getVertices() == rhs.getVertices() &&
lhs.getWires() == rhs.getWires();
}
2. Implement the loop, I would recommend using iterators
This is just one way of doing it
if (allParts.size() > 1) {
for(auto partIt = std::begin(allParts); partIt != std::end(allParts); partIt++) {
for(auto partIt2 = std::next(partIt); partIt2 != std::end(allParts);) { // Manual increasing because we erase stuff
if(*partIt == *partIt2) { // Previously implemented equility operator
partIt->AddAmount(1);
partIt2 = allParts.erase(partIt2); // If erase, use the returned iterator as your next `Part`
} else {
partIt2++; // Only increment if nothing was erased (when you erase iterators get invalidated)
}
}
}
}

Iterating over vector of custom object with two conditions

Using C++11, I'd like to iterate over a vector and return a type that indicates that the index was not found.
I am use to the traditional for(;;) loop and specifying the index manually, as my code shows below.
inline std::size_t ItemList::FindItem(Items& Item)
{
for (std::size_t i = 0; i < ItemVector.size(); i++)
{
if (ItemVector[i]->GetId() == Item.GetId() && !ItemVector[i]->GetName().compare(Item.GetName()))
{
return i + 1;
}
}
return 0;
}
I'm also having to increment the index +1 in order to return a value of 0 (to accommodate unsigned size_t) to indicate the calling method that the index was not found (I understand this is asinine). I am assuming it would be more suitable to return something more like std::end()?
Would using a C++11 iterator approach be more efficient? The vector will populate to a large number and the find needs to be quick.
You could use std::find_if and work with iterators:
auto it = std::find_if(ItemVector.begin(), ItemVector.end(),
[&Item](Items *value) {
return value->GetId() == Item.GetId() && !value->GetName().compare(Item.GetName());
}
);
Then you can simply test if it != ItemVector.end() to know if you found something.
There will likely be no (or very small) difference between this and your version in term of speed, but it is a cleaner way to check if something was found or not.
Yes, an iterator would be the way to do this, you're actually writing your own version of find_if You could instead do:
find_if(cbegin(ItemVector), cend(ItemVector), [&](const auto& i){ return i.GetId() == Item.GetId() && i.GetName() != Item.GetName(); })
You can test whether the result of this function was found by testing for equality with cend(ItemVector).
Additionally if you need to find the index of the item you can pass this result after cbegin(ItemVector) to: distance
Live Example
My solution for double search condition that Lambda has multiple parameters in find_if
bool check_second_loop(FullFrame *image_track, guint64 object_id, bool *deletion)
{
auto itr= std::find_if(image_track->track_ids.begin(),
image_track->track_ids.end(),
[object_id](const guint64& a)
{
return a == object_id;
});
if (itr != image_track->track_ids.end())
{
image_track->track_ids.erase(itr);
if(image_track->track_ids.size()==0)
{
*deletion = true;
}
return true;
}
else
return false;
}
bool check_first_loop(guint64 object_id, gint source_id)
{
bool deletion = false;
auto it = find_if(full_frame_list.begin(), full_frame_list.end(),
[object_id, &deletion, source_id](FullFrame &x)
{
return check_second_loop(&x, object_id, &deletion)
&& x.camera_number == source_id;
});
if (it != full_frame_list.end())
{
// Found
return true;
}
else
return false;
}

C++ finding something in vector

I have an vector like this:
struct RIFT_MONSTER_SPAWN
{
DWORD dwWorldId;
D3DXVECTOR3 vPos;
};
vector<RIFT_MONSTER_SPAWN> vecMSpawn;
As you can see it will hold 2 values dwWorldId and D3DXVECTOR3 vPos;
vPos will hold x,y,z value.
Now what I want to do is looping true the vector and if the worldId matches the worldId that is beeing passed. It should return the vPos that is releated to the worldId.
If have use std::find find_all and count.
But it always returns the error
binary == no operator found which takes a left hand operator of type
So I am an bit stuck on this. Any help would be appreciated.
With kind regards.
Here is the code that is giving me problems
void CRiftMatch::GetMoverSpawnPoints(dwWorldId)
{
std::vector<RIFT_MONSTER_SPAWN> vecSpawn = CRiftMng::GetInstance()->m_vecMSpawnPoint;
std::vector<RIFT_MONSTER_SPAWN>::iterator it = std::find(vecSpawn.begin(), vecSpawn.end(), dwWorldId);
OUTPUTDEBUGSTRING("\n GetMoverSpawn %d", *it);
}
m_vecMSpawnPoint is defined in .h file as
vector<RIFT_MONSTER_SPAWN> m_vecMSpawnPoint;
Also to fill it i am using this code
while (Lua.TableLoop(-2))
{
RIFT_MONSTER_SPAWN rSpawnPoint;
rSpawnPoint.dwWorldId = static_cast<int>(CScript::GetDefineNum(Lua.GetFieldToString(-1, "dwWorldId")));
rSpawnPoint.vPos.x = static_cast<float>(Lua.GetFieldToNumber(-1, "x"));
rSpawnPoint.vPos.y = static_cast<float>(Lua.GetFieldToNumber(-1, "y"));
rSpawnPoint.vPos.z = static_cast<float>(Lua.GetFieldToNumber(-1, "z"));
m_vecMSpawnPoint.push_back(rSpawnPoint);
Lua.Pop(1);
}
You have to modify your struct to be able compare values during find:
struct RIFT_MONSTER_SPAWN
{
DWORD dwWorldId;
D3DXVECTOR3 vPos;
bool operator () ( const RIFT_MONSTER_SPAWN & m ) const
{
return m.dwWorldId == dwWorldId;
}
};
RIFT_MONSTER_SPAWN monsterToFind;
monsterToFind.dwWorldId = dwWorldId;
it = std::find_if( vecSpawn.begin(), vecSpawn.end(), monsterToFind);
Maybe just a type, but in your code, you have
void CRiftMatch::GetMoverSpawnPoints(dwWorldId)
but it should be
void CRiftMatch::GetMoverSpawnPoints(DWORD dwWorldId)
You could pass a predicate (which can be a lambda) to std::find_if so code
auto it = std::find_if(vecSpawn.begin(), vecSpawn.end(),
[&] (const struct RIFT_MONSTER_SPAWN& sp)
{return sp.dxWorldId == dwWorldIt;});
But in such a case I would simply use a for loop (because I find that more readable):
int ix=0;
for (auto&sp : vecSpawn) {
if (sp.dxWorldId == dwWorldIt)
return vecSpawn.begin() + ix;
ix++;
}
With range/v3, you may simply do
auto it = ranges::find(vecSpawn, dwWorldIt, &RIFT_MONSTER_SPAWN::dxWorldId);
else you have to use more verbose
auto it = std::find_if(vecSpawn.begin(), vecSpawn.end(),
[&](const RIFT_MONSTER_SPAWN& e) {
return e.dxWorldId == dwWorldIt;
});

C++ how to check if an element of all objects stored in a vector have the same value?

For example I have an vector of objects and they all have attribute PointX so I want to check if all of them have the same value of PointX and if they have it should return true or false whatever.
So in a kind of pseudo-code:
IF ( Object[1].getPointX() == Object[2].getPoint(x) == Object[3].getPoint(x) ){
return true;
}
The problem is that I have more than 55 objects so there has to be a way to compare them without writting them all individually. I know there must be with a for loop but still I have no idea.
thanks in advance.
edit:
#awesomeyi your suggestion seems the easiest and more adequate to my needs (I'm not saying the others are not but I think they are too complicated for a newb like me) but it's not working, even if Xpoints are all the same or different it always returns false. here's what i have:
bool allKilled(){
auto checkpointx = Invader[0].getIfActive();
for(int i = 0; i < 55; ++i){
if(Invader[i].getIfActive() != checkpointx){
return false;}
}
}
the getIfActive() just returns if its true or false, and i want this method to return true if all Active (attribute of the object) of all objects are all false.
Something like this:
auto refx = Object[0].getPointX();
bool b = std::all_of(Object.begin(),
Object.end(),
[refx](TheTypeOfTHeElements& e)
{ return e.getPointX() == ref; });
Obviously you need to check that Objects is not empty.
For loop?
auto checkpointx = Object[0].getPointX();
for(int i = 1; i < Object.size(); ++i){
if(Object[i].getPointX() != checkpointx)
return false;
}
I'd do it something like this:
template<typename Objects, typename Comparison>
bool is_all_same( Objects&& objects, Comparison&& comp ) {
using std::begin; using std::end;
auto&& b = begin(objects);
auto&& e = end(objects);
if (b == e)
return true;
auto&& first = *b;
for( auto&& cur = std::next(first); cur != e; ++cur ) {
if (!comp( first, cur ))
return false;
}
return true;
}
use:
bool all_same_x = is_all_same( objs, []( Obj const& left, Obj const& right )->bool {
return left.getPointX() == right.getPointX();
});
where objs is some container or range containing objects of type Obj.
The above code should work on all standard containers, C style arrays, custom containers that support the new for loops, and is close to optimal performance wise.

how to sort a std::set using more than one key

I need to copy a set to another one based on more than one key.
the keys are used to -collectively- maintain the uniqueness as well as the order of elements in the set.
My class:
class LaneConnector {
public:
const Lane* getLaneFrom() const {
return From;
}
const Lane* getLaneTo() const {
return To;
}
private:
Lane* From;
Lane* To;
}
my functor:
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* rhs, const LaneConnector* lhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
bool key1 = a->getLaneID() < b->getLaneID();
bool key2 = a->getLaneParent->ID() < b->getLaneParent->ID();
bool key2 = a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID();
//remind you that I NEED the elements to be in ascending order of
//getLaneParent->getParent->ID() ,a->getLaneParent->ID() and then a->getLaneID()
//duplicate elements are the ones which have all three keys same and need to be discarded
return (key1 && key2 && key3); //which dont seem to be working
}
};
and my source and origin sets:
const std::set<LaneConnector*> src = ..... ; //the getter give me a const version
std::set<sim_mob::LaneConnector *, MyLaneConectorSorter> dest;
and how I fill it up:
for(std::set<sim_mob::LaneConnector*>::iterator it = tempLC.begin(); it != tempLC.end(); it++)
{
dest.insert(*it);//I know I can insert it right at the time of declaration, but keep it like this for now...please
}
your kind help would be highly appreciated.
Since getting operator< for multiple tests right is rather hard, I advocate my way of doing this with tuple (in this case with make_tuple instead of tie since we're dealing with temporaries returned from functions):
#include <tuple>
struct MyLaneConectorSorter {
bool operator() (const LaneConnector* lhs, const LaneConnector* rhs) const
{
const Lane* a = lhs->getLaneFrom();
const Lane* b = rhs->getLaneFrom();
auto const* pa = a->getLaneParent();
auto const* pb = b->getLaneParent();
return std::make_tuple(a->getLaneID(), pa->ID(), pa->getParent()->ID()) <
std::make_tuple(b->getLaneID(), pb->ID(), pb->getParent()->ID())
}
This should work and you can get tuple and make_tuple from Boost too, if your compiler doesn't offer them yet.
You need to prioritorise your key field comparisons... only if the most important field is equal, then you compare the second most important - if that's equal then you compare the third most important etc.. As soon as there's an inequality, you return true or false as appropriate. So, it's not a && operation, it should be ? : or an if-else chain, as in:
return lhs.key1 < rhs.key1 ? true :
rhs.key1 < lhs.key1 ? false :
lhs.key2 < rhs.key2 ? true :
rhs.key2 < lhs.key2 ? false :
...
false;
For the set to operate correctly, you must ensure the keys are never equal - so that last false is never actually used.
If you have three member foo, bar and baz to compare on, this is a common way to compare them:
return lhs.foo < rhs.foo
|| lhs.foo == rhs.foo && (lhs.bar < rhs.bar
|| lhs.bar == rhs.bar && lhs.baz < rhs.baz);
Do you see the pattern? ;)
I have problem understanding your sorting rules, but if the relation is a simple sub-sort than the code should look like this:
if (a->getLaneID() < b->getLaneID())
return true;
else if (a->getLaneID() == b->getLaneID())
{
if (a->getLaneParent->ID() < b->getLaneParent->ID())
return true;
// etc...
}
return false;
Your class MyLaneConnectionSorter has a flaw.
std::set expects a comparison class that can order elements. So your comparison function must provide behaviour similar to less functor or operator<, i.e. either a < b or a > b (which is b < a) or a == b (which is !(a < b) && !(a > b))
If we take your comparison function, it will consider Lanes (6, 5, 4) and (7, 3, 4) (in format (PPID, PID, ID)) to be equal, because neither one is less than another. So you need to compare like this:
if (a->getLaneParent->getParent->ID() < b->getLaneParent->getParent->ID()) return true;
else if (a->getLaneParent->getParent->ID() > b->getLaneParent->getParent->ID()) return false;
else {
if (a->getLaneParent->ID() < b->getLaneParent->ID()) return true;
else if (a->getLaneParent->ID() > b->getLaneParent->ID()) return false;
else {
return (a->getLaneID() < b->getLaneID());
}
}