3

So I have the following lambda function to search my vector of strings for a certain sub string within a string.

Like some random strings "water" "attic" "cat" "pool"

I would want to return all strings that contain "at" .

auto iter = std::find_if(myVector.begin(),
                         myVector.end(),
                         [subSequence](std::string s) -> bool { return (subSequence == s); });
if ( iter != myVector.end() ){
    std::cout << *iter << "\n";     
}

My question is, what is the best way to extend this to loop thru my whole vector? I am new to this, and everything I've tried doesn't work. Is this even feasible, or do I need to consider another approach?

  • 1
    What do you mean by *`std::find_if` is only to be used on one element of a container*? `find_if` is iterating over the entire `vector`, but you're not doing anything meaningful within the lambda. You probably want something like `[&subSequence](std::string const& s) { return s == subSequence; }` instead. – Praetorian Oct 20 '15 at 00:46
  • 3
    Your code makes no sense, `subSequence == subSequence` will always return `true`. Depending on how often you're searching the container you might want to consider an `unordered_set` or `unordered_multset` anyway. – user657267 Oct 20 '15 at 00:47
  • I guess you are looking for ways to return multiple elements of the sequence that meet your criteria. You may want to take a look at lower_bound, upper_bound, equal_range as explained here http://stackoverflow.com/questions/23554509/rationale-for-stdlower-bound-and-stdupper-bound – ramana_k Oct 20 '15 at 00:53

2 Answers2

4

Try this: (Updated to match updated question)

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> myVector = {"cat","bye","what's up?"};
    std::string subString = "at";

    //Create a lambda function to check if a string contains subString
    //Uses s.find(subString), which returns the position of subString in s,
    //  and checks that it doesn't equal the end of the string 
    auto containsSubString = [subString](std::string s){
        return s.find(subString) != std::string::npos;};

    auto iter = std::find_if(myVector.begin(),
                             myVector.end(),
                             containsSubString);

    //Use a while loop, checking whether iter is at the end of myVector
    //Do a find_if starting at the item after iter, std::next(iter)
    while (iter != myVector.end())
    {
        std::cout << *iter << std::endl;
        iter = std::find_if(std::next(iter),
                            myVector.end(),
                            containsSubString);
    }

    return 0;
}

Expected Output:

cat
what's up?

Issues with your code:

  1. Your lambda simply compared subSequence to subSequence, so it always returned true.
  2. You were using std::find_if correctly, but the trick is to just stick it into a while loop that checks whether you've hit the end of the vector yet.
  3. While it doesn't hurt to include -> bool in your lambda, it's not strictly necessary, because the compiler can infer that information from the lambda's definition (s.find(subString) != std::string::npos will always return a bool). You only have to explicitly mention type when it might be ambiguous to the compiler (or if you want to make it extra clear to other programmers who might read your code).

Edit: As seen in @Yakk 's answer, it is actually better to use std::next(iter) instead of iter + 1, as I originally had it.
This is because std::next(iter) will work for all types of C++ STL containers, whereas just incrementing the iterator does not work on all containers (though it does work on the most common, such as std::array or std::vector).

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Numeri
  • 1,027
  • 4
  • 14
  • 32
  • Thank you. Your answer made me realize I wasn't doing what I thought. I am searching for a substring within a string. I updated my question to reflect this. –  Oct 20 '15 at 01:11
  • OK, yes, then you will need to look a little further. Do you need to find all strings in a vector that contain the substring? In that case, find out how to find if a string has a substrings, and then put that in a lambda. I actually don't remember how to check if a string contains a substring off the top of my head, but I'd recommend a good search engine. :) Good luck! – Numeri Oct 20 '15 at 01:14
  • @user5447431 I've updated my answer to match your new question (though perhaps you should have made a new question—I'm not sure what the StackOverflow community thinks about that). Anyway, I hope this helps! – Numeri Oct 20 '15 at 17:07
  • Thank you! This is exactly what I was looking to do. –  Oct 21 '15 at 01:29
  • Great! Feel free to accept it if it works for you! ;) – Numeri Oct 21 '15 at 14:05
0

Replace [subSequence](std::string) -> bool { return (subSequence == subSequence); } with [&](std::string const&str) -> bool { return (subSequence == str); } and call me in the morning.

If you want the next element, and it did not return .end(), just replace the begin clause with std::next(it), where it is the last returned element. Either in a loop, or recursively.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524