Find in vector

Nov 12, 2019 at 4:54am
I need to find a substring.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

int main() {
	int n;
	vector<string> words;
	string substring, word;
	cout << "Input the number of words: " << endl;
	cin >> n;
	cout << "write a substring" << endl;
	cin >> substring;
	cout << "Write the words: " << endl;
	//for (std::string word; std::cin >> word)
	for (int i = 0; i < n; i++) {
		cin >> word;
		words.push_back(word);
	}
	int it;
	it=find(words.begin(), words.end(),substring);

}

Nov 12, 2019 at 5:56am
What you need is an iterator to string not int value.

replace you last 2 lines with this:


1
2
3
4
5
6
7
8
9
	auto it = std::find_if(words.begin(), words.end(), [&](const std::string& ref)
	{
		return static_cast<bool>(!std::strcmp(ref.c_str(), substring.c_str()));
	});

	if (it != words.end())
		std::cout << "string found: " << (*it).c_str();
	else
		std::cout << "nothing found" << std::endl;


additional headers needed:
 
#include <cstring> // std::strcmp  


EDIT:

I forgot, but I think this can be written in more simple way (without cstring header):
1
2
3
4
5
	
auto it = std::find_if(words.begin(), words.end(), [&](const std::string& ref)	
{
	return ref == substring;
});

Last edited on Nov 12, 2019 at 6:04am
Nov 12, 2019 at 7:39am
@malibor: Your second example seeks whole words.

The objective is to seek from a vector such an element (word) that contains a substring. To seek substring from each word (up to first match).

To find a substring from string: http://www.cplusplus.com/reference/string/string/find/

1
2
3
4
auto it = std::find_if( words.begin(), words.end(), [&](const std::string& ref)	
  {
    return std::string::npos != ref.find( substring );
  });



There are regular expressions too, that allow fancier "finds".
Last edited on Nov 12, 2019 at 7:42am
Nov 12, 2019 at 7:50am
@keskiverto
yes, you are correct about this, I already thought to give such example but problem is that multiple strings in a vector may have same substring, and how are you going to handle that case? what to return to caller?

If OP really needs that functionality then neither of our examples will work, instead additional function may be needed that returns ie. new vector of strings that contains all the strings which contain a substring.

EDIT:
here is updated version that fill vector with strings that contain a substring:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
        // note: taking address not copies!
        // pointers may become invalid if original vector relocates internally
	std::vector<const std::string*> finds = {};

	std::for_each(words.begin(), words.end(), [&](const std::string& ref)
		{
			if (std::string::npos != ref.find(substring))
				finds.push_back(&ref);
		});

	if (!finds.empty())
		for (const auto& ref : finds)
			std::cout << *ref << std::endl;
        else std::cout << "nothing found";

Last edited on Nov 12, 2019 at 8:28am
Nov 12, 2019 at 11:21am
Great, it's run, but I didn't understand any parts.

Why you used {}?
 
vector<const string*> finds = {};


What a function of?
 
for (const auto& ref : finds)
Nov 12, 2019 at 2:40pm
victorio wrote:
I need to find a substring.

@malibor:
A word was requested, and that is what find_if() returns: first "valid" word (or none).

"All words that contain substring" is different goal.


T x = {}; declares variable named 'x' that has type 'T' and initializes it with empty value.
C++11 allows also syntax: T x {};

However, the std::vector is a class that has default constructor. The initializer is unnecessary.


1
2
3
for ( const auto& ref : finds ) {
  std::cout << *ref << std::endl;
}

This is ranged for-syntax that was added by C++11.
1
2
3
4
5
6
7
8
9
10
11
vector<string> words;
// ranged for
for ( string w : words ) {
  std::cout << w << std::endl;
}

// old for
for ( size_t x=0; x < words.size(); ++x ) {
  string w = words[x];
  std::cout << w << std::endl;
}


The auto keyword in C++11 (and later) makes the compiler deduce typename. The 'finds' has type std::vector<const std::string*> and thus 'auto' in the loop (probably) means const std::string*

Note: Constant reference to a pointer yields no advantage in this case.
Nov 12, 2019 at 3:58pm
I understand now, thank you!
Nov 12, 2019 at 4:16pm
@keskiverto
apologies, even now, I'm not sure what the real intention of OP was.

@victorio
vector<const string*> finds = {};
keskiverto explain it all, but I have this habit mainly because of analyzer warnings from:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md
Always initialize variables, use initialization lists for member variables.

and it's not even correct. it should be:
vector<const string*> finds{};
which is just being explicit, and good practice.
Topic archived. No new replies allowed.