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.
Related
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 :)
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
}
}
I'm new to C++ so I'm having trouble figuring out how to best remove an object from a vector while still iterating through it.
Basically, I need to iterate through two vectors. For every item, if the ID's match, I can remove them.
//For every person, check to see if the available bags match:
for(std::vector<Person>::iterator pit = waitingPeopleVector.begin(); pit != waitingPeopleVector.end(); ++pit) {
for(std::vector<Bag>::iterator bit = waitingBagsVector.begin(); bit != waitingBagsVector.end(); ++bit) {
int pId = pit->getId();
int bId = bit->getId();
if(pId == bId){
//a match occurs, remove the bag and person
}
}
}
Working with iterators is a bit confusing, I know I can use the .erase() function on my vectors, but I can't really pass pit or bit. Any help appreciated. Thanks
From the standard:
The iterator returned from a.erase(q) points to the element
immediately following q prior to the element being erased. If no such
element exists, a.end() is returned.
I would use it in something like using the erase method:
std::vector<Person>::iterator pit = waitingPeopleVector.begin();
std::vector<Bag>::iterator bit = waitingBagsVector.begin();
while (pit != waitingPeopleVector.end())
{
bool didit;
while (bit != waitingBagsVector.end())
{
didit = false;
if (pit->getId() == bit->getId() && !didit)
{
bit = waitingBagsVector.erase(bit);
pit = waitingPeopleVector.erase(pit);
didit = true;
}
else
{
++bit;
}
}
if (didit)
continue;
else
++pit;
}
Using the erase-remove idiom will achieve this objective, the below offers an (untested) way using lambdas and <algorithm> functions to remove elements from wPL which have the same ID as wBL. It shouldn't be too much effort to extend this to both lists. Note, we have used std::list instead of std::vector for faster removal.
std::list<Person> wPL;
std::list<Bag> wBL;
//...
wPL.erase(std::remove_if(wPL.begin(), wPL.end(),
[&wBL](auto x) { return std::find_if(wBL.begin(), wBL.end(), [](auto y)
{ return x.getId() == y.getId();); }), wPL.end() };
Is there an efficient way of executing a function if any string inside a vector contains a substring?
Something along these lines
if(vector.contains(strstr(currentVectorElement,"substring"))) {
//do something
}
if(vector.contains(strstr(currentVectorElement,"substring2"))) {
//do something else
}
The only thing I can think of is iterating over each string and check if the substring exists.
You can use std::find_if with a lambda expression
if(std::find_if(vec.begin(), vec.end(), [](const std::string& str) { return str.find("substring") != std::string::npos; }) != vec.end()) {
...
}
What you need is std::search()
http://en.cppreference.com/w/cpp/algorithm/search
I have a structure like this:
struct client
{
string name;
double money;
};
I also have 2 predicates:
bool less_10(const client& a)
{
return a.money < 10;
}
bool not_a(const client& a)
{
return a.name.at(0) != 'A';
}
In my main function I use this to filter out the result stored in vector client_list (everyone with money < 10 (choice 1) or everyone with name not start with A (else))
if (choice_filter == 1)
{
vector<client>::iterator it3;
it3 = find_if(client_list.begin(), client_list.end(), less_10);
while (it3 != client_list.end())
{
**client_list.erase(it3);
it3 = find_if(it3 + 1, client_list.end(), less_10);
}
client_list.erase(it3);**
}
else
{
vector<client>::iterator it4;
it4 = find_if(client_list.begin(), client_list.end(), not_a);
while (it4 != client_list.end())
{
**client_list.erase(it4);
it4 = find_if(it4 + 1, client_list.end(), not_a);
}
client_list.erase(it4);**
}
I notice that if I erase first, then find_if, i'll lost the last client. So i added 1 more line to erase, but the program crashes as iterator is now at the end, cant erase.
Is there any way to get around this? I want to keep using find_if with predicates as well as while loop like above as they are required.
As others have said, std::remove_if is the best solution. If
you're doing this for pedagogical reasons (which I suspect is
the case, given these particular predicates): you're on the
right track. The only issue is that client_list.erase
invalidates the iterator. But since it returns an iterator to
the element immediately after the element it erased, you can use
something like:
std::vector<Client>::iterator it
= std::find_if( client_list.begin(), client_list.end(), predicate );
while ( it != client_list.end() ) {
it = client_list.erase( it );
it = std::find_if( it, client_list.end(), predicate );
}
And you don't want to call erase after the loop. The iterator
designates the end, where there is no element to be erased.
The typical way to go is to use a temporary vector:
vector<client> tmp;
for (...)
{
if(predicate(it))
tmp.push_back(*it);
}
client_list.swap(tmp);
This is similar to what Chris suggested in a comment, although that solution would first move elements to the end of the vector and then truncate them from there. I'm not sure if that doesn't change the order on the way, just check the documentation. Depending on what you want, either could do the work though.
If you used a different container like list<> that did not invalidate all iterators in erase(), you could do this:
it = c.begin();
end = c.end();
while(it != end)
{
if(predicate(*it))
{
c.erase(it++);
}
else
{
++it;
}
}
Note that if you call erase(), you invalidate that iterator still, hence the iterator is first incremented and erase() is called with the former value using the postfix increment.
I also agree with chris, to using std::remove_if:
{
remove_if(client_list.begin(), client_list.end(), less_10);
}
But if you want to reinvent the wheel:
{
vector<client>::iterator it3 = client_list.begin();
while (true)
{
it3 = find_if(it3, client_list.end(), less_10);
if (it3 == client_list.end()) {
break;
}
it3 = client_list.erase(it3);
}
}