C++ finding a value using find_if - c++

I am trying to find if a string words contains any instruction form machine_opTable, a vector of pair-string,int.
What should my lambda function look like in find_if ?
If you have any other approach, please let me know.
As of now, my code looks like ...
# define RX 4
# define RR 2
...
vector < pair <string, int> > machine_opTable = { {"L", RX},
{ "A", RX},
{"ST", RX}
};
words = " L 1, SMB1";
string inMachineop;
for ( auto instruction: words){
inMachineop = find_if ( begin(machine_opTable), end(machine_opTable), [] (const pair<string,int>& p) { return ( p.first == instruction ? p.first : "NOTFOUND"); });
}
I would love to return iterator pointing to that pair... Please show me how it's done.
Thank you.

Return value from find_if according to reference is
An iterator to the first element in the range for which pred does not return false.
pred is your lambda and it must return bool value.
If you want to use instruction in your lambda, you have to capture this variable
[instruction] (const pair<string,int>& p) { return p.first == instruction; }
and the call of find_if when we know it returns iterator looks as follows
auto it = find_if ( begin(machine_opTable), end(machine_opTable), [instruction] (const pair<string,int>& p) { return p.first == instruction; });
if (it != machine_opTable.end())
{
// access to found pair
}
else
; // pair not found

I see a bug here:
for ( auto instruction: words){
You iterate by characters, not by words here. You need to split by space first. Try to use this code for that task https://stackoverflow.com/a/27511119/9187525
Bugs related to find_if() was explained by others :)

Related

Finding and erasing a value from a std::vector holding std::map elements

First, I have the following two objects, both filled with data:
std::vector<std::map<std::uint8_t, std::uint8_t>> x1;
std::vector<std::map<std::uint8_t, std::uint8_t>> x2;
My objective is to search inside x2 (by the key), checking if any value from x1 doesn't exist inside x2, and then erase it from x1.
I tried with the following code snippet, but to no avail (it doesn't compile!):
for (auto i = x1.begin(); i != x1.end(); ++i)
{
auto it = std::find(x2.begin(), x2.end(), i);
if (it == x2.end())
{
x1.erase(i);
}
}
What am I doing wrong? Could you please share some insights on how to solve this problem?
There are several problems with your code:
std::find() searches for a single matching element, which in this case means you have to give it a std::map to search for. But you are passing in the i iterator itself, not the std::map that it refers to. You need to dereference i, eg:
auto it = std::find(x2.cbegin(), x2.cend(), *i);
When calling x1.erase(i), i becomes invalidated, which means the loop cannot use i anymore - not for ++i, not for i != x1.end(). You need to save the new iterator that erase() returns, which refers to the next element after the one being erased. Which means you also need to update your loop logic to NOT increment i when erase() is called, eg:
for (auto i = x1.cbegin(); i != x1.cend(); )
{
auto it = std::find(x2.cbegin(), x2.cend(), *i);
if (it == x2.cend())
i = x1.erase(i);
else
++i;
}
lastly, when using std::find(), you are comparing entire std::map objects to each other. If you are interested in comparing only the keys, try something more like this:
for (auto i = x1.cbegin(); i != x1.cend(); )
{
const auto &m1 = *i:
auto it = std::find_if(m1.cbegin(), m1.cend(),
[&](const decltype(m1)::value_type &m1_pair) { // or (const auto &m1_pair) in C++14...
return std::find_if(x2.cbegin(), x2.cend(),
[&](const decltype(x2)::value_type &m2){ // or (const auto &m2) in C++14...
return m2.find(m1_pair.first) != m2.cend();
}
);
}
);
if (it == m1.cend())
i = x1.erase(i);
else
++i;
}
You can also go a little bit functional: Playground
#include <algorithm>
#include <functional>
// removes maps from x1, that are equal to none of x2 maps
auto remove_start = std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(std::equal_to(), x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());
EDIT: To check keys only, change std::equal_to to a custom lambda
auto keys_equal = [](auto& m1, auto& m2){
return m1.size() == m2.size()
&& std::equal(m1.begin(), m1.end(), m2.begin(),
[](auto& kv1, auto& kv2){ return kv1.first == kv2.first; });
};
// removes maps from x1, that are equal to none of x2 maps
auto remove_start =
std::remove_if(x1.begin(), x1.end(), [&](const auto& x1_map){
return std::none_of(x2.begin(), x2.end(),
std::bind(keys_equal, x1_map, std::placeholders::_1));
});
x1.erase(remove_start, x1.end());

Search for a variable from vector of objects

struct ABC
{
int a;
string b;
};
I have a vector of objects to the above struct. And want to search the vector based on variable "b"?
I have logic as below.
vector<ABC> vec = ...;//vec has my objects
for(vector<ABC>::iterator it = vec.begin();
it != vec.end();
++it)
{
if(search_str == (it->b))//search string is my string which i need to search
{
}
}
I have extensively tested the above code and it works. I want to know if there is a better way to achieve this. Maybe using find().
Simple, readable, lifted from Sam's comment:
auto found = std::find_if(vec.begin(), vec.end(), [&](auto const &e) {
return e.b == search_str;
});
And now found is an iterator to the first matching element, or vec.end() if none was found.
You can also use range based for loops in some cases, give you much clearer code.
for (auto const &p : vec)
{
if (p == search_str)
{
//--- Handle the find ---//
//if you want to stop...
break;
}
}
One of the better method to compare two strings is using compare method in C++.
Suppose you want to compare two strings S1 and S2. You can use equality operator( == ) as you have already used.
But using std::string::compare() function has it's own benefit.
We can not only compare two strings but can also check if one is less or greater.
std::string::compare() function return an int:
zero if S1 is equal to S2.
less than zero if S1 is less than S2.
greater than zero if S1 is greater than S2.
So your code can be formatted as:
vector<ABC> vec = ...;//vec has my objects
for(vector<ABC>::iterator it = vec.begin(); it != vec.end(); ++it){
if(!search_str.compare(it->b))
{
//match found
}
}

finding the return value of the lower_bound

I am trying to use a lower_bound to find out if value is inside a vector of pointers to struct. I am using
auto it = lower_bound( myVector.begin() , myVector.end() , value , comparer() );
comparer function looks like
struct comparer
{
bool operator ()(Property * ms, int const i) const
{
return ms -> ID < i;
};
};
and i want to check if the element with said ID was found. How can i check it?
I tried using
if( (*it) -> ID == value ) {
return false;
}
but this is throwing segmentation fault, Is there any way how to check if element is already there?
If you just want to check if the object exists, use std::binary_search:
bool exists = std::binary_search(myVector.begin(), myVector.end(), value, comparer());
That said, if you want the iterator, you'll need to not only check if the value matches but you'll also have to first check if you got something other than the end iterator:
auto it = std::lower_bound(myVector.begin(), myVector.end(), value, comparer());
if (it != myVector.end() && (*it)->ID == value) {
return false;
}
If you do get end(), then that dereference is undefined behavior, which could manifest as a segmentation fault.

Correct Iteration with deletion in List syntax

I'm currently writing a program that uses lists at a point in said program i want to iterate through 3 three lists a, b and c, and delete any element in b and c if it appears in a. Im doing it as such:
//remove elements from OpenList that are in ClosedList
for(list<Node> :: iterator cloIt = ClosedList.begin(); cloIt != ClosedList.end(); cloIt++)
{
for(list<Node> :: iterator opIt = OpenList.begin(); opIt != OpenList.end(); opIt++)
{
for(list<Node> :: iterator neigIt = Neighbour.begin(); neigIt != Neighbour.end(); neigIt++)
{
if (*cloIt == *opIt)
{
opIt = OpenList.erase(opIt);
}
if (*cloIt == *neigIt)
{
neigIt = Neighbour.erase(neigIt);
}
}
}
}
However this is causing me to get an "List iterator not incrementable" error
How could i fix this?
From your erase call, you want to
remove OpenList items if they are found in ClosedList list
remove Neighbour items if they are found from ClosedListlist
You'd better separate code into two loops, instead of nested loops, for example:
1.remove OpenList items if they are found in ClosedList list
for(auto cloIt = ClosedList.begin(); cloIt != ClosedList.end(); ++cloIt)
{
OpenList.remove_if([&](const Node& n){ return n == *colIt; } );
}
2.remove Neighbour items if they are found from ClosedListlist
for(auto cloIt = ClosedList.begin(); cloIt != ClosedList.end(); ++cloIt)
{
Neighbour.remove_if([&](const Node& n){ return n == *colIt; } );
}
Obvious previous code is duplicated, you could write a common function for that:
void RemoveItem(std::list<Node>& node_list, std::list<Node>& node_list2)
{
for(auto cloIt = node_list2.begin(); cloIt != node_list2.end(); ++cloIt)
{
node_list.remove_if([&](const Node& n){ return n == *colIt; } );
}
}
Now you could call:
RemoveItem(OpenList, CloseList);
RemoveItem(Neighbour, CloseList);
Update:
Don't forget to define operator== for Node type, for example if node has getId interface:
bool operator==(const Node& lhs, const Node& rhs)
{
return lhs.getId() == rhs.getId();
}
How could i fix this?
The best way is to use standard algorithms and let them do the iteration, search, and/or the conditional removal for you.
You could use std::list's remove_if() member function with a lambda predicate that checks if the element is contained in list a:
#include <algorithm>
// ...
b.remove_if(
[&a] (Node const& n)
{
return (std::find(begin(a), end(a), n) != a.end());
});
Same for removing elements from c if they are contained in a.
Another possibility is to use std::for_each() to iterate over all elements of a and remove them from b and c:
#include <algorithm>
// ...
std::for_each(begin(a), end(a),
[&b, &c] (Node const& n)
{
b.remove(n);
c.remove(n);
});
You've correctly used the return value of .erase to obtain the new iterator, but forgot that this iterator gets ++'d immediately at the end of the current iteration of your loop; if the result of .erase was .end, then this is an invalid operation.
(You're actually very fortunate that you get a diagnostic for attempting to increment your now-invalid iterators — the standard guarantees absolutely nothing about this case.)
You need to ++ only when you didn't .erase.
The general pattern looks like this:
for (typename list<T>::iterator it = l.begin(), end = l.end(); it != end; )
{
// ^^ NB. no "it++" in the loop introduction!
if (foo(*it)) {
// condition satisfied; do the erase, and get the next
// iterator from `.erase` and NOT through incrementing
it = l.erase(it);
}
else {
// no erasure; do the increment only in this case
it++;
}
}
You could avoid the problem altogether by using standard algorithms, as Andy suggests.

Search vectors in returned iterator

Context:
I perform a std::find with a std::string on a <-string,vector->map. It then returns me an iterator of vectors, I keep the returned iterator in a const-iterator.
Problem:
I now want to iterate through the returned const-iterator, and string compare every vector at index 0. so something like:
while (iterator != map.end())
if ( myStr == iterator.at(0) )
break;
else
iterator++
That approach works just fine for me, I was wondering if there is a more elegant way of doing this, am I missing something?
Thanks for your help with this =]
Instead of explicitly coding the search you could use std::find_if():
std::vector<std::vector<std::string>> vstring
{
{ "no", "yes" },
{ "help", "yes" },
{ "true", "false" }
};
const std::string myStr = "help";
auto f = std::find_if(vstring.begin(), vstring.end(),
[&](std::vector<std::string>const & vs)
{
return !vs.empty() && myStr == vs[0];
});
if (f != vstring.end())
{
// Found.
}
See demo at http://ideone.com/nkI7fk .
One way to make this more "elegant" would be something like this:
// C++11 allows `using` to be used instead of `typedef`
using map_type = std::map<std::string, std::vector<some_type>>;
// First find the starting point of our secondary search
const auto itr = map.find(some_string);
// Do secondary search
const auto found = std::find_if(itr, map.end(),
[](const map_type::value_type& pair)
{
return (!pair.second.empty() &&
pair.second[0] == myStr);
});
if (found != map.end())
{
// Found the item
}
There is a very poor way I can imagine. It's not ordinary and should(or even must) never be used. Overloade comparison operator for vector, so it would compare only 0 positions. And then use map::find() method. Just fun.