int main()
{
map<string, int> M;
vector<string> V;
set<string> S;
ifstream inFile("sample_doc.txt");
copy( istream_iterator<string>(inFile), istream_iterator<string>(), back_inserter(V) );
ifstream inFile2("stopwords.txt");
copy( istream_iterator<string>(inFile2), istream_iterator<string>(), inserter( S, S.begin() ) );
for_each( V.begin(), V.end(), [&](string & s){ S.count(s) == 0 ? M[s]++ : true; } );
}
in the for_each statement, when I pass in the lambda function above, it gives me the following error.
error: no matching function for call to ‘for_each(std::vector<std::basic_string<char> >::iterator, std::vector<std::basic_string<char> >::iterator, main()::__lambda0)’
for_each( V.begin(), V.end(), [&](string & s){ S.count(s) == 0 ? M[s]++ : true; } );
can someone tell me how to fix it? thanks a lot.
This here built on VS2017, maybe you have forgot some header?
#include <map>
#include <algorithm>
#include <set>
#include <iterator>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
void foo()
{
using namespace std;
map<string, int> M;
vector<string> V;
set<string> S;
ifstream inFile("sample_doc.txt");
copy(istream_iterator<string>(inFile), istream_iterator<string>(), back_inserter(V));
ifstream inFile2("stopwords.txt");
copy(istream_iterator<string>(inFile2), istream_iterator<string>(), inserter(S, S.begin()));
for_each(V.begin(), V.end(), [&](string & s) { S.count(s) == 0 ? M[s]++ : true; });
}
Related
Basically I have a load of words in my string vector vector<string> words.
I need to make a function that searches for all the words with "ly" throughout my vector and return them, for example (golly, helpfully, mostly, nearly).
How do I use the std::find_if function to do this or is there any other way that I can do this?
I also need to find words that are longer than 7 letters in my vector, do I still use the std::find_if function with >=7 or something else?
First of all, there is a more appropriate algorithm in the standard library called std::copy_if than the std::find_if (for what you have asked).
Secondly, you need to get a different list of words asper different cases. This sounds like having a template function which wraps the std::copy_if and also provide a way to give the custom compare (e.g. a lambda function) functionalities.
Therefore I would suggest something like as follows:
#include <algorithm> // std::copy_if
#include <iterator> // std::cbegin, std::cend
template<typename Container, typename Predicate>
auto getElelmentsOf(const Container& container, const Predicate condition) /* noexcept */
{
Container result;
std::copy_if(std::cbegin(container), std::cend(container), std::back_inserter(result),
condition);
return result;
}
Now you could write something like
// all the words with "ly"
const auto words_with_ly = [](const auto& ele) {
return ele.find(std::string{ "ly" }) != std::string::npos;
};
const auto elemtsOfLy = getElelmentsOf(words, words_with_ly); // function call
// find words that are longer than 7 letters
const auto words_with_size_7_more = [](const auto& ele) { return ele.size() > 7; };
const auto elemtsOfsize7More = getElelmentsOf(words, words_with_size_7_more); // function call
(See a Live Demo Online)
You can use std::copy_if to get all elements that satisfy some conditions.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // for std::copy_if
#include <iterator> // for std::back_inserter
using std::vector;
using std::string;
int main(void) {
vector<string>words={
"golly", "hoge", "lyric", "helpfully",
"mostly", "abcdefg", "nearly", "terrible"
};
vector<string> res_ly, res_7;
// get all words that contains "ly"
std::copy_if(words.begin(), words.end(), std::back_inserter(res_ly),
[](const string& x){ return x.find("ly") != string::npos; });
// get all words that are longer than 7 letters
std::copy_if(words.begin(), words.end(), std::back_inserter(res_7),
[](const string& x){ return x.length() > 7; });
// print what we got
std::cout << "words with \"ly\":\n";
for (const string& s : res_ly) std::cout << " " << s << '\n';
std::cout << "\nwords longer than 7 letters:\n";
for (const string& s : res_7) std::cout << " " << s << '\n';
return 0;
}
Output:
words with "ly":
golly
lyric
helpfully
mostly
nearly
words longer than 7 letters:
helpfully
terrible
If you want to use std::find_if, you can repeat searching like this:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm> // for std::find_if
#include <iterator> // for std::next
using std::vector;
using std::string;
int main(void) {
vector<string>words={
"golly", "hoge", "lyric", "helpfully",
"mostly", "abcdefg", "nearly", "terrible"
};
vector<string> res_ly;
// get all words that contains "ly"
for (vector<string>::iterator start = words.begin(); ;) {
vector<string>::iterator next = std::find_if(start, words.end(),
[](const string& x){ return x.find("ly") != string::npos; });
if (next == words.end()) {
break;
} else {
res_ly.push_back(*next);
start = std::next(next, 1);
}
}
// print what we got
std::cout << "words with \"ly\":\n";
for (const string& s : res_ly) std::cout << " " << s << '\n';
return 0;
}
I could suggest the following solution.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
std::vector<std::string> copy_strings( const std::vector<std::string> &v, const std::string &s )
{
auto present = [&s]( const auto &item )
{
return item.find( s ) != std::string::npos;
};
auto n = std::count_if( std::begin( v ), std::end( v ), present );
std::vector<std::string> result;
result.reserve( n );
std::copy_if( std::begin( v ), std::end( v ),
std::back_inserter( result ),
present );
return result;
}
int main()
{
std::vector<std::string> v =
{
"golly", "helpfully", "mostly", "nearly"
};
auto result = copy_strings( v, "ly" );
for (const auto &item : result )
{
std::cout << item << ' ';
}
std::cout << '\n';
return 0;
}
The program output is
golly helpfully mostly nearly
I'm trying to take a vector of strings and remove every character that's not a letter (number, symbols, etc.) I'm also not trying to use loops.
So here's an example of a vector:
std::vector<std::string> a = {"he2llo*", "3worl$d"};
And I want the string returned to look like this:
std::vector<std::string> a = {"hello", "world"};
Right now I'm trying to use the transfrom and erase algorithms, but I can't get the syntax right.
This is obviously incomplete, but it's the basic setup of what I have so far:
int trim(std::vector<std::string> a){
std::transform(a.begin(), a.end(), a.erase())
You can use std::for_each on the vector and then use the erase-remove idiom on the strings, as follows
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
int main() {
std::vector<std::string> a = {"he2llo*", "3worl$d"};
std::for_each(a.begin(), a.end(),[](auto & str){
str.erase(std::remove_if(str.begin(), str.end(), [] (auto & character){return !isalpha(character);}), str.end());
});
for(auto const & el : a)
std::cout << el << " ";
}
The output:
hello world
Recursively..
#include <iostream>
#include <vector>
std::string remove_bad_characters(std::string input, std::string result)
{
if (input.size() == 0)
{
return result;
}
if (!isalpha(input[0]))
{
return remove_bad_characters(input.substr(1), result);
}
result += input[0];
return remove_bad_characters(input.substr(1), result);
}
std::vector<std::string> edit_bad_strings(std::vector<std::string> input, std::size_t index)
{
if (index == input.size())
{
return input;
}
input[index] = remove_bad_characters(input[index], "");
return edit_bad_strings(input, index + 1);
}
int main() {
std::cout<<remove_bad_characters("h!ello!", "")<<"\n";
std::vector<std::string> good = edit_bad_strings(std::vector<std::string>{"h!ell#o", "wo0rl-d"}, 0);
for (std::string str : good)
{
std::cout<<str<<" ";
}
return 0;
}
You can use std::for_each instead of loop to traverse each element.
Then you can apply std::transform on each element of vector.
You can refer -
http://www.cplusplus.com/reference/algorithm/for_each/
http://www.cplusplus.com/reference/algorithm/transform/
Here's one way you can do it with the algorithm header and lambda functions:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main() {
std::vector<std::string> strArr = {"he2llo*", "3worl$d"};
std::transform(strArr.begin(), strArr.end(), strArr.begin(), [](std::string &str) -> std::string {
str.erase(std::remove_if(str.begin(), str.end(), [](char chr) -> bool {
return ! isalpha(chr);
}), str.end());
return str;
});
std::for_each(strArr.begin(), strArr.end(), [](const auto &str) {
std::cout << str << '\n';
});
return 0;
}
The outer lambda processes each string to erase specific characters by using remove_if, while the inner lambda just controls which characters are removed. Whether that's more readable than a loop-based solution is open to debate :-)
You can use C++20 std::erase_if
#include<string>
#include<vector>
#include<iostream>
#include<algorithm>
int main() {
std::vector<std::string> a = {"he2llo*", "3worl$d"};
std::transform(a.begin(), a.end(), a.begin(),
[](auto& str) {
std::erase_if(str, [](const auto& chr){return !isalpha(chr);});
return std::move(str);
});
for (const auto& str: a){
std::cout << str << std::endl;
}
}
You can try it in different ways with STL <algorithm>s, i implemented a functor to process each word :
#include <iostream>
#include <vector>
#include <cctype>
#include <algorithm>
class Processing{
public:
std::string operator()(std::string& value){
for_each(value.begin(), value.end(), [&](char v) mutable throw() ->
void {
auto fetch = std::find_if( value.begin(), value.end(), [&](char v)mutable throw()->
bool{
return(!isalpha(v));
});
if(*fetch){
value.erase( fetch );
}
});
return value;
}
};
int main()
{
std::vector<std::string> values = {"44h%ello333","%w%or333ld21"};
std::for_each(values.begin(),values.end(), Processing());
std::for_each(values.begin(),values.end(), [](std::string& value)->
void {
std::cout<<value<<" ";
});
return 0;
}
I have a 2D vector of strings and want to count how many times a certain word is repeated. For example:
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
vector< vector<string> > vec(4, vector<string>(4, "word") );
count( vec.begin(), vec.end(), "certain word" );
}
But the above gives errors. How can I do this?
You need to run count on search individual vector and sum the results:
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
vector< vector<string> > vec(4, vector<string>(4, "string of words") );
size_t sum = 0;
for(auto& v: vec) {
sum += count( v.begin(), v.end(), "certain word" );
}
}
I have a map like std::map<std::string, std::vector<int> >. I want to create one vector out of all the sub-vectors from values of the map. I can do it using the loops but I wanted to use the boost::range library where I can pipe the input of one transform to another so that the code is more readable and succinct. I tried something like below but needed some help to do it correctly.
Thank you
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <iterator>
#include <vector>
#include <map>
#include <iostream>
struct GetMapValue
{
template<typename T1, typename T2>
const T2& operator()( const std::pair<T1,T2>& key_val ) const
{
return key_val.second;
}
};
struct GetINTs
{
GetINTs(std::vector<int> aVec1)
{
aMyVec = aVec1;
}
void operator()( const std::vector<int>& val )
{
aMyVec.insert( val.end(), val.begin(), aMyVec.end() );
}
private:
std::vector<int> aMyVec;
};
int main(){
std::map<std::string, std::vector<int> > x;
std::vector<int> y, z;
std::vector<int> temp;
temp.push_back(1);
temp.push_back(1);
x.insert(std::make_pair("one", temp));
temp.clear();
temp.push_back(2);
temp.push_back(2);
x.insert(std::make_pair("two", temp));
temp.clear();
temp.push_back(3);
temp.push_back(3);
x.insert(std::make_pair("three", temp));
boost::copy( x | boost::adaptors::transformed(GetMapValue), std::back_inserter(y) );
boost::copy( y | boost::adaptors::transformed(GetINTs), std::back_inserter(z) );
}
The adaptor already exists and is called
boost::adaptors::map_keys
boost::adaptors::map_values
Here's a sample:
Live On Coliru
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <map>
int main() {
std::map<std::string, std::vector<int> > x {
{ "hello", { 1,2,3 } },
{ "world", { 4,5,6 } },
};
boost::copy(x | boost::adaptors::map_keys, std::ostream_iterator<std::string>(std::cout << "Keys: ", " "));
for (auto& vec : x | boost::adaptors::map_values)
boost::copy(vec, std::ostream_iterator<int>(std::cout << "\nValues: ", " "));
}
Prints
Keys: hello world
Values: 1 2 3
Values: 4 5 6
I would like to know if there is an elegant way or a built-in function to convert vector<double> to vector<string>. What I've done is simple
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
std::vector<std::string> doubeVecToStr(const std::vector<double>& vec)
{
std::vector<std::string> tempStr;
for (unsigned int i(0); i < vec.size(); ++i){
std::ostringstream doubleStr;
doubleStr << vec[i];
tempStr.push_back(doubleStr.str());
}
return tempStr;
}
int main( int argc, char* argv[] )
{
std::vector<double> doubleVec;
doubleVec.push_back(1.0);
doubleVec.push_back(2.1);
doubleVec.push_back(3.2);
std::vector<std::string> doubleStr;
doubleStr = doubeVecToStr(doubleVec);
for (unsigned int i(0); i < doubleStr.size(); ++i)
std::cout << doubleStr[i] << " ";
std::cout << std::endl;
return 0;
}
There are many ways, but a standard solution is to use std::transform with a lambda using std::to_string for the conversion :
std::transform(std::begin(doubleVec),
std::end(doubleVec),
std::back_inserter(doubleStr),
[](double d) { return std::to_string(d); }
);
And you can wrap that in a function template to make it work with any Standard compliant container :
template<class IteratorIn, class IteratorOut>
void to_string(IteratorIn first, IteratorIn last, IteratorOut out)
{
std::transform(first, last, out,
[](typename std::iterator_traits<IteratorIn>::value_type d) { return std::to_string(d); } );
}
Or in C++14, with a generic lambda :
template<class IteratorIn, class IteratorOut>
void to_string(IteratorIn first, IteratorIn last, IteratorOut out)
{
std::transform(first, last, out, [](auto d) { return std::to_string(d); } );
}
And call it with any container (i.e. it works with std::list<int>, for instance) :
to_string(std::begin(doubleVec), std::end(doubleVec), std::back_inserter(doubleStr));
Notes :
If you don't have a C++11 compiler, write your own to_string function template :
Example:
template<class T>
std::string my_to_string(T v)
{
std::stringstream ss;
ss << v;
return ss.str();
}
And use it in a similar way :
std::transform(doubleVec.begin(),
doubleVec.end(),
std::back_inserter(doubleStr),
my_to_string<double> );
You should reserve() the memory in the output vector to avoid reallocations during std::transform() :
e.g. do this :
std::vector<std::string> stringVec;
stringVec.reserve(v.size()); // reserve space for v.size() elements
Live demo
Using copy and ostream_iterator:
#include <vector>
#include <iostream>
#include <sstream>
#include <iterator>
int main()
{
std::vector<double> numbers{1.0, 2.1, 3.2};
std::stringstream output;
std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<double>(output, " "));
std::cout << output.str() << std::endl;
}
In general, if you have a container of T and want to create a container of U from the container of T, as others have mentioned the algorithm to look for is std::transform.
If you are not using C++ 11, Here is std::transform usage:
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <sstream>
std::string Transformer(double d)
{
std::ostringstream doubleStr;
doubleStr << d;
return doubleStr.str();
}
int main()
{
std::vector<double> doubleVec;
doubleVec.push_back(1.0);
doubleVec.push_back(2.1);
doubleVec.push_back(3.2);
std::vector<std::string> doubleStr;
std::transform(doubleVec.begin(), doubleVec.end(), std::back_inserter(doubleStr), Transformer);
std::copy(doubleStr.begin(), doubleStr.end(), std::ostream_iterator<std::string>(std::cout, " "));
}
Output:
1 2.1 3.2