How to use lambda for std::find_if - c++

I am trying to use std::find_if to find an object that matches some criteria. Consider the following:
struct MyStruct
{
MyStruct(const int & id) : m_id(id) {}
int m_id;
};
...
std::vector<MyStruct> myVector; //... assume it contains things
MyStruct toFind(1);
std::vector<MyStruct>::iterator i = std::find_if(myVector.begin(), myVector.end(), ???);
I am not sure what to put in the ???
All the examples I have seen have a lambda that uses a hard-coded value to check for the ID. What I want is to return the iterator/success only if the id of toFind matches the id of one of the items in the vector.
All the examples I have see don't show me how to pass the two parameters
EDIT
Additional info
There are two different scenarios I have to use this for
One in which there is an == operator for the struct
and another in which there is no operator == for the struct - and i can't create one because the criteria for finding a match for this scenario is not as rigid as would be used for an equivalence operator.
(And thanks to all who responded; I was able to use find() in one case and with your help was able to use find_if() for the other)

Try this:
std::find_if(
myVector.begin(), myVector.end(),
[&toFind](const MyStruct& x) { return x.m_id == toFind.m_id;});
Alternatively, if you had defined an appropriate == overload for MyStruct, you could just use find:
std::find(myVector.begin(), myVector.end(), toFind); // requires ==
The find_if version is usually best when you have some kind of heterogeneous lookup, for example if you were just given an int, not a value of MyStruct.

This is where the lambda capture comes into play. Besides saying what type of parameters are to be passed to the lambda you can also say what existing variables are to be used to construct the lambda with. So in this case you would have something like
std::vector<MyStruct>::iterator i = std::find_if(myVector.begin(),
myVector.end(),
[&](const auto& val){ return val.m_id == toFind.m_id; } );
So the [&] says capture all variables used in the body of the lambda by reference. The (const auto& val) makes the operator() of the lambda a template and lets you take in any type. Then in the body we compare what is passed in from find_if to toFind.

You may use the following:
MyStruct toFind(1);
std::vector<MyStruct>::iterator i =
std::find_if(myVector.begin(), myVector.end(),
[&](const auto& e) { return e.id == toFind.id; });

Do as following:
std::find_if(myVector.begin(), myVector.end(),
[&toFind] (const auto &ele) { return ele.m_id == toFind.m_id}; );

Related

Lambda with multiple parameters in find_if

I've got a vector with objects of a custom data type. One of the fields of this data type is an enum. I want to make sure that for all enum values at least one entry is added to the vector.
I want to use std::find_if() to check if the vector has an entry for a certain enum value. But I'm not sure how to write & use the lambda correctly. Here's what I got:
struct CustomType
{
EnumType type {EnumType::DEFAULT};
int x;
};
std::vector<CustomType> tmp;
// fetch values from database and add them to the vector
// for some EnumType values there might not be a record added to the vector
auto hasEnumType = [](const CustomType& customType, const EnumType& enumType) -> bool
{
return (customType.type == enumType);
};
// What do I replace '?' with to get the current position
if (std::find_if(tmp.begin(), tmp.end(), hasEnumType(?, EnumType::VALUE1)) != tmp.end())
{
//add CustomType with VALUE1 to vector
}
If you want to check against a fixed value, you can simply put it inside the lambda.
if ( std::find_if(tmp.begin(), tmp.end(), [](CustomType const & customType)
{ return customType.type == EnumType::VALUE; }) )
{ /* do something */}
If you want, in a sense, pass it to lambda as parameter, you can set it in an external variable and "capture" it by reference
auto enumType = EnumType::VALUE;
// capture variables by references --------V
if ( std::find_if(tmp.begin(), tmp.end(), [&](CustomType const & customType)
{ return customType.type == enumType; }) )
{ /* do something */ }
std::find_if takes a unary predicate as its third parameter. If you provide binary predicate it will fail type check.
One way to do this is to convert your binary predicate to unary predicate by capturing one of its parameters. To automate it for all enum values create a function that returns another function.
auto getTypeChecker = [](const EnumType enumType)
{
return [enumType](const CustomType& customType)
{
return (customType.type == enumType);
};
};
then declare a function that takes care of inserting enum values that are not present in the vector -
void insert_if_not_present(std::vector<CustomType>& vec, const EnumType type)
{
if(std::none_of(begin(vec), end(vec), getTypeChecker(type)))
{
//add CustomType with type to vec
}
}
Now call insert_if_not_present once for each enum value.
Note, here I have used std::none_of for convenience. You can use std::find_if in place of std::none_of in smae manner.

std::transform with lambda: skip some items

I have some C++11 code like
std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(), names.end(), std::inserter(first_to_last_name_map, first_to_last_name_map.begin()), [](const std::string& i){
if (i == "bad")
return std::pair<std::string, std::string>("bad", "bad"); // Don't Want This
else
return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
});
where I'm transforming a vector to a map using std::transform with a lambda function. My problem is that sometimes, as shown, I don't want to return anything from my lambda function, i.e. I basically want to skip that i and go to the next one (without adding anything to the map).
Is there any way to achieve what I'm thinking about? I can use boost if it helps. I want to avoid a solution where I have to do a pre-process or post-process on my vector to filter out the "bad" items; I should only need to look at each item once. Also, my actual logic is a bit more complicated than the if/else as written, so I think it would be nice to keep things encapsulated in this std::transform/lambda model if possible (though maybe what I'm trying to achieve isn't possible with this model).
EDIT: Just to emphasize, I'm looking to perform this operation (selectively processing vector elements and inserting them into a map) in the most efficient way possible, even if it means a less elegant solution or a big rewrite. I could even use a different map data type depending on what is most efficient.
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.
You can simply have a first/last pass with std::remove_if. E.g.
std::vector<std::string> names;
std::map<std::string, std::string> first_to_last_name_map;
std::transform(names.begin(),
std::remove_if(names.begin(),
names.end(),
[](const std::string &str){
return str=="bad";
}),
std::inserter(first_to_last_name_map,
first_to_last_name_map.begin()),
[](const std::string& i){
return std::pair<std::string, std::string>(i.substr(0,5), i.substr(5,5));
});
Note that remove_if simply shifts the removed items past the iterator it returns.
You can use boost::adaptors::filtered to first filter the vector of the elements you don't want, before passing it to transform.
using boost::adaptors::filtered;
boost::transform(names | filtered([](std::string const& s) { return s != "bad"; }),
std::inserter(first_to_last_name_map, first_to_last_name_map.begin()),
[](std::string const& i) { return std::make_pair(i.substr(0,5), i.substr(5,5)); });
Live demo

Counting element properties in a vector

I have a vector of objects, and I'd like to count how many of them contain a certain property.
I'm fairly sure this can be done with the STL but I couldn't find an example. I could of course use a loop and count myself, but I need to do this many times and I'd prefer a concise way of doing this.
I'm looking to do something like the pseudo code below
class MyObj {
public:
std::string name;
}
std::vector<MyObj> objects
int calledJohn = count(objects,this->name,"jonn")
If you're looking to count how many objects have a certain property, std::count_if is the way to go. std::count_if takes a range to iterate over and the functor object that will determine if the object has the value:
auto calledJohn = std::count_if(std::begin(objects), std::end(objects),
[] (const MyObj& obj) { return obj.name == "John"; });
Use std::count_if
auto n = std::count_if(objects.begin(), objects.end(),
[](const MyObj& o) { return o.name == "jonn";});
There is a function std::count_if in the algorithm header that does exactly that for you. You have to provide an iterator range (so in your case objects.begin and objects.end) and a predicate that could be a lambda function or any other callable object:
auto number = std::count_if(objects.begin(), objects.end(), [](const MyObj &object){if(/*your condition*/){return true;}});

Modern way to filter STL container?

Coming back to C++ after years of C# I was wondering what the modern - read: C++11 - way of filtering an array would be, i.e. how can we achieve something similar to this Linq query:
var filteredElements = elements.Where(elm => elm.filterProperty == true);
In order to filter a vector of elements (strings for the sake of this question)?
I sincerely hope the old STL style algorithms (or even extensions like boost::filter_iterator) requiring explicit methods to be defined are superseded by now?
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.
In C++20, use filter view from the ranges library: (requires #include <ranges>)
// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })
lazily returns the even elements in vec.
(See [range.adaptor.object]/4 and [range.filter])
This is already supported by GCC 10 (live demo). For Clang and older versions of GCC, the original range-v3 library can be used too, with #include <range/v3/view/filter.hpp> (or #include <range/v3/all.hpp>) and the ranges::views namespace instead of std::ranges::views (live demo).
A more efficient approach, if you don't actually need a new copy of the list, is remove_if, which actually removes the elements from the original container.
I think Boost.Range deserves a mention too. The resulting code is pretty close to the original:
#include <boost/range/adaptors.hpp>
// ...
using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
{ return elm.filterProperty == true; });
The only downside is having to explicitly declare the lambda's parameter type. I used decltype(elements)::value_type because it avoids having to spell out the exact type, and also adds a grain of genericity. Alternatively, with C++14's polymorphic lambdas, the type could be simply specified as auto:
auto filteredElements = elements | filtered([](auto const& elm)
{ return elm.filterProperty == true; });
filteredElements would be a range, suitable for traversal, but it's basically a view of the original container. If what you need is another container filled with copies of the elements satisfying the criteria (so that it's independent from the lifetime of the original container), it could look like:
using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
{ return elm.filterProperty == true; }), back_inserter(filteredElements));
Improved pjm code following underscore-d suggestions:
template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
Cont result;
std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
return result;
}
Usage:
std::vector<int> myVec = {1,4,7,8,9,0};
auto filteredVec = filter(myVec, [](int a) { return a > 5; });
My suggestion for C++ equivalent of C#
var filteredElements = elements.Where(elm => elm.filterProperty == true);
Define a template function to which you pass a lambda predicate to do the filtering. The template function returns the filtered result. eg:
template<typename T>
vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate)
{
vector<T> result;
copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
return result;
}
to use - giving a trivial examples:
std::vector<int> mVec = {1,4,7,8,9,0};
// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });
// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });

C++ remove_if on a vector of objects

I have a vector (order is important) of objects (lets call them myobj class) where I'm trying to delete multiple objects at a time.
class vectorList
{
vector<*myobj> myList;
};
class myobj
{
char* myName;
int index;
bool m_bMarkedDelete;
}
I was thinking that the best way to do this would be to mark specific myobj objects for deletion and then call myList.remove_if() on the vector. However, I'm not exactly sure how to use predicates and such for this. Should I create a member variable in the object which allows me to say that I want to delete the myobj and then create a predicate which checks to see if the member variable was set?
How do I implement the predicate as a part of the vectorList class?
Should I create a member variable in the object which allows me to say
that I want to delete the myobj and then create a predicate which
checks to see if the member variable was set?
Haven't you already done that? Isn't that what m_bMarkedDelete is for? You would write the predicate like this:
bool IsMarkedToDelete(const myobj & o)
{
return o.m_bMarkedDelete;
}
Then:
myList.erase(
std::remove_if(myList.begin(), myList.end(), IsMarkedToDelete),
myList.end());
Or, using lambdas:
myList.erase(
std::remove_if(myList.begin(), myList.end(),
[](const myobj & o) { return o.m_bMarkedDelete; }),
myList.end());
If your class doesn't actually have that member, and you're asking us if it should, then I would say no. What criteria did you use to decide to mark it for deletion? Use that same criteria in your predicate, for example:
bool IndexGreaterThanTen(const myobj & o)
{
return o.index > 10;
}
note -- The functions I've written are of course invalid since all your members are private. So you'll need some way to access them.
A predicate is basically a conditional comparison. It can be a function or object. Here's an example using new C++ lambdas. This code will go through the vector and remove the values equal to 3.
int arg[6] = {1, 2, 3, 3, 3, 5};
std::vector<int> vec(arg, arg+6);
vec.erase(
std::remove_if(
vec.begin(), vec.end(),
[](int i){ return i == 3;}),
vec.end());
Edit: For pointers let's say you had a vector or interfaces you could set them to nullptr then remove them in a batch with pretty much the same code. In VS2008 you won't have lambdas so make a comparison predicate function or struct instead.
bool ShouldDelete(IAbstractBase* i)
{
return i == nullptr;
// you can put whatever you want here like:
// return i->m_bMarkedDelete;
}
std::vector<IAbstractBase*> vec;
vec.erase(
std::remove_if(
vec.begin(), vec.end(),
ShouldDelete),
vec.end());