How to Filter Items from a Std::Map

How to filter items from a std::map?

You could use erase(), but I don't know how BOOST_FOREACH will handle the invalidated iterator. The documentation for map::erase states that only the erased iterator will be invalidated, the others should be OK. Here's how I would restructure the inner loop:

Actions::iterator it = _actions.begin();
while (it != _actions.end())
{
if (expired(*it))
{
bar(*it);
Actions::iterator toerase = it;
++it;
_actions.erase(toerase);
}
else
++it;
}

Modern way to filter STL container?

See the example from cplusplus.com for std::copy_if:

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_if evaluates the lambda expression for every element in foo here and if it returns true it copies the value to bar.

The std::back_inserter allows us to actually insert new elements at the end of bar (using push_back()) with an iterator without having to resize it to the required size first.

Filter the synthesized attribute through a std::map in a boost spirit semantic action

Reposting from comment

Try qi::_val = phx::ref(myMap)[qi::_1] rather than qi::_val = phx::at(myMap, qi::_1).

How can I search an std::map using a key of a different type

With C++14 you can use heterogeneous lookup.

If you'd like to find an element with key that compares equivalent to the argument of std::map::find, you should provide a Comparator as a third template parameter which should have Comparator::is_transparent denoted as a type. It should also contain bool operator() comparing your map key with any other type you'd like.

Funny description aside, here's an example:

struct X
{
int id;
int subid;
};

struct Details {};

struct Comparator
{
using is_transparent = std::true_type;

// standard comparison (between two instances of X)
bool operator()(const X& lhs, const X& rhs) const { return lhs.id < rhs.id; }

// comparison via id (compares X with integer)
bool operator()(const X& lhs, int rhs) const { return lhs.id < rhs; }
bool operator()(int lhs, const X& rhs) const { return lhs < rhs.id; }

// Same thing with Y
bool operator()(const X& lhs, const Y& rhs) const { return lhs.id < rhs.getId(); }
bool operator()(const Y& lhs, const X& rhs) const { return lhs.getId() < rhs.id; }
};

int main()
{
std::map<X, Details, Comparator> detailsMap = {
{ X{1, 2}, Details{} },
{ X{3, 4}, Details{} },
{ X{5, 6}, Details{} }
};

// it1 and it2 point to the same element.
auto it1 = detailsMap.find(X{1, 2});
auto it2 = detailsMap.find(1);

std::cout << detailsMap.size() << std::endl;
std::cout << std::boolalpha << (it1 == detailsMap.end()) << std::endl; // false
std::cout << std::boolalpha << (it1 == it2) << std::endl; // true
}

Note however that GCC didin't implement it until revision 219888.

returning a set of keys in the map matching the criteria

I think your solution is rather good: it is clear, and except if you can "guess" hash values based on the condition, I don't think you could be much more performant. However, you could change your function to make it more generic:

template<typename TKey, typename TValue, typename Predicate>
void filter (const map<TKey, TValue> & m,
set<TKey> & result,
Predicate & p)
{
typename map<TKey,TValue>::const_iterator it = m.begin();
typename map<TKey,TValue>::const_iterator end = m.end();

for( ; it != end ; ++it)
{
TKey key = it->first;
if (p(key))
result.insert(key);
}
}

Your example can then be writen using a functor as predicate:

struct Contains {
Contains(const string & substr) : substr_(substr) {}

bool operator()(const string & s)
{
return s.find(substr_) != string::npos;
}

string substr_;
};

The call to filter will then look like this:

map<string, Obj> m;
// Insert in m
set<string> res;
filter(m, res, Contains("stringToFind"));

std::transform with lambda: skip some items

template<class Src, class Sink, class F>
void transform_if(Src&& src, Sink&& sink, F&& f){
for(auto&& x:std::forward<Src>(src))
if(auto&& e=f(decltype(x)(x)))
*sink++ = *decltype(e)(e);
}

Now simply get a boost or std or std experiental optional. Have your f return an optional<blah>.

auto sink = std::inserter(first_to_last_name_map, first_to_last_name_map.begin());
using pair_type = decltype(first_to_last_name_map)::value_type;

transform_if(names, sink,
[](const std::string& i)->std::optional<pair_type>{
if (i == "bad")
return {}; // Don't Want This
else
return std::make_pair(i.substr(0,5), i.substr(5,5));
}
);

My personal preferred optional actually has begin end defined. And we get this algorithm:

template<class Src, class Sink, class F>
void polymap(Src&& src, Sink&& sink, F&& f){
for(auto&& x:std::forward<Src>(src))
for(auto&& e:f(decltype(x)(x)))
*sink++ = decltype(e)(e);
}

which now lets the f return a range, where optional is a model of a zero or one element range.

How to filter object in c++

Keep it simple:

for (auto& c : myCells)
if (c.valueX > 5)
resultCells.push_back(c);

For your specific requirement ("write a function that gets vector of Cells, operator (gt - greater than, le - less or equal, and so on), name of a variable and an integer, and returns vector of Cells"), assuming "get" means "take as parameter":

vector<Cell> filter(vector<Cell> const& cells,
function<bool(int,int)> const& op,
int Cell::* var,
int i) {
vector<Cell> results;
for (auto& c : cells)
if (op(c.*var, i))
results.push_back(c);
return results;
}

Usage:

resultCells = filter(myCells, greater<int>(),  &Cell::valueX, 5);
resultCells = filter(myCells, less<int>(), &Cell::valueY, 5);
resultCells = filter(myCells, equal_to<int>(), &Cell::ID, 5);


Related Topics



Leave a reply



Submit