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
Related
For example I have vector {'a','a','b','b','c'} and I want to get the most letters which is a and b but this code the output is a;
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int getMostFrequentElement(std::vector<char> &arr)
{
if (arr.empty())
return -1;
std::sort(arr.begin(), arr.end());
auto last_int = arr.front();
auto most_freq_int = arr.front();
int max_freq = 0, current_freq = 0;
for (const auto &i : arr) {
if (i == last_int)
++current_freq;
else {
if (current_freq > max_freq) {
max_freq = current_freq;
most_freq_int = last_int;
}
last_int = i;
current_freq = 1;
}
}
if (current_freq > max_freq) {
max_freq = current_freq;
most_freq_int = last_int;
}
return most_freq_int;
}
int main(){
std::vector<char> arr = {'a','a','b','b','c'};
char ret = getMostFrequentElement(arr);
std::cout << "Most frequent element = " << ret;
}
May I know why my output becomes a instead a and b?
input vector arr{'a','a','b','b','c'}
expected output is a and b
but my output is a
Your function returns only the first most frequent character as an integer in a sorted vector.
For starters the implementation of the function is not good. The function shall not sort the passed by reference vector. It is the owner of the vector decides whether to sort the vector before calling the function. The function shall not modify the passed to it vector.
If you want that the function would return all most frequent characters in a vector then you need to change the function essentially.
For example the function can look the following way as it is shown in the demonstrative program below.
#include <iostream>
#include <vector>
#include <map>
#include <iterator>
#include <algorithm>
std::vector<char> getMostFrequentElement( const std::vector<char> &v )
{
std::vector<char> result;
std::map<char, size_t> m;
for ( const auto &c : v ) ++m[c];
auto it = std::max_element( std::begin( m ), std::end( m ),
[]( const auto &p1, const auto &p2 )
{
return p1.second < p2.second;
} );
if ( it != std::end( m ) )
{
for ( const auto &p : m )
{
if ( p.second == it->second ) result.push_back( p.first );
}
}
return result;
}
int main()
{
std::vector<char> v = { 'a', 'a', 'b', 'b', 'c' };
auto result = getMostFrequentElement( v );
for ( const auto &c : result ) std::cout << c << ' ';
std::cout << '\n';
return 0;
}
The program output is
a b
The answer from Vlad is good and should be accepted.
I would like to show an additional, more "mordern" C++ solution.
The Function body is rather compact and consists only of 3 lines of code. It will count all occurences of char and sort it in decreasing order regarding the occurence.
So, the caller of this function can show all kind of information. In the example below, I show all topmost elements.
But all kind of other evaluations may be shown.
Please see:
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <set>
#include <iterator>
#include <unordered_map>
// Writing some aliases to prevent later typing work and make the code a little bit more readable. ---------------------
using DataType = char;
using CounterType = unsigned int;
using Pair = std::pair<DataType, CounterType>;
using Counter = std::unordered_map<DataType, CounterType>;
using Data = std::vector<DataType>;
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using CountedAndSorted = std::multiset<Pair, Comp>;
// ----------------------------------------------------------------------------------------------------------------------
CountedAndSorted getMostFrequentElement(Data& data) {
// Count
Counter counter{};
for (const char c : data) counter[c]++;
// Return counted and sorted result
return {counter.begin(), counter.end()};
}
// ------------------------
// Test/Driver code
int main() {
// Test Data
Data d = { 'a', 'a', 'b', 'b', 'c' };
// Calculate result
auto result = getMostFrequentElement(d);
// Show output
for (const auto& [c, count] : result) if (count == result.begin()->second) std::cout << c << ' ';
}
how can i delete the rest of the characters?
s.erase(std::unique(s.begin(), s.end()), s.end());
This only deletes the duplicated characters and doesn't remove the first presence of the character.
Example: "Hello World"
would return "he wrd"
There's no builtin for this function, but you can write your own generic algorithm to accomplish this:
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
template <class C>
auto erase_if_duplicate(C& c)
{
using T = typename C::value_type;
const auto begin = c.begin();
const auto end = c.end();
std::unordered_map<T, std::size_t> count{};
std::for_each(
begin, end,
[&] (const T& v) { ++count[v]; });
const auto it = std::remove_if(
begin, end,
[&] (const T& v) { return count.at(v) > 1; });
return c.erase(it, end);
}
int main()
{
// example usage
std::string s{"hello world"};
erase_if_duplicate(s);
std::cout << s; // he wrd
}
Try it on godbolt.org
From what I understand, you want to remove all occurrences of a character from a string if it has duplicates (its count is bigger than 2), and the removal is not case-sensitive (meaning H will be treated as h).
So, you can create a frequency map of all character, and if its count is >1 then remove all of its occurrences with erase():
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
string x; getline(cin, x);
map<char, int> freq;
for (size_t i = 0; i < x.size(); i++)
{
x[i] = tolower(x[i]); //lowercase all the character
freq[x[i]]++; //frequency of character
}
x.erase(remove_if(x.begin(), x.end(),
[&] (const char& c) { return freq[c] > 1; }),
x.end());
cout << x;
}
Output :
Hello world
he wrd
Another example:
ABbcDdDEfF
ace
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 cpp vector containing separate words and I need to count how many times a word appears using a list. I try to iterate through the list but failing with the comparison of the two STL containers, whether the following word is already in my list or not. If not, I want to add that word to my list with an appearance of 1. I have a struct that counts the times a word appeared in the text.
The following code returns a list of words and numbers, but not each in my vector and I can't see why.
struct counter{
string word;
int sum = 1;
counter(){};
counter(string word): word(word){};
};
list<counter> list_count(vector<string> &text){
list<counter> word_count;
list<counter>::iterator it = word_count.begin();
for(string t:text){
if(it != word_count.end()){
it -> sum++;
} else {
word_count.push_back(counter(t));
}
++it;
}
return word_count;
}
Thank you in advance.
list<counter> list_count(const vector<string>& text) {
list<counter> word_count;
for (const string& t : text) {
auto it = std::find_if(word_count.begin(), word_count.end(),
[&](const counter& c){ return c.word == t; });
if (it != word_count.end()) {
it -> sum++;
} else {
word_count.push_back(counter(t));
}
}
return word_count;
}
Untested code.
You are not actually searching the std::list at all. On every loop iteration through the std::vector, you need to search the entire std::list from front to back, eg:
#include <string>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;
struct counter {
string word;
int sum = 1;
counter(const string &word): word(word) {}
};
list<counter> list_count(const vector<string> &text) {
list<counter> word_count;
for(const string &t: text) {
// perform an actual search here!
list<counter>::iterator it = find_if(
word_count.begin(), word_count.end(),
[&](counter &c){ return (c.word == t); }
);
if (it != word_count.end()) {
it->sum++;
} else {
word_count.emplace_back(t);
}
}
return word_count;
}
Live Demo
That being said, a std::list is a poor solution for counting elements. A better solution is to use a std::(unordered_)map instead (unless you need to preserve the order of the words found, which neither one will do), eg:
#include <string>
#include <map>
#include <vector>
using namespace std;
map<string, int> list_count(const vector<string> &text) {
map<string, int> word_count;
for(const string &t: text) {
word_count[t]++;
}
return word_count;
}
Live Demo (using std::map)
Live Demo (using std::unordered_map)
You are trying to use an inefficient approach. The standard class template list does not have random access to its elements. Each new element is appended to the end of the list. To find whether an element is already present in the list elements of it are traversed sequentially.
It would be much efficiently to use the standard container std::map . Moreover in this container words will be ordered.
For example you could declare
std::map<std::string, size_t> counters;
Nevertheless if you want to use the list then the function can look as it is shown in the demonstrative program below.
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &c ) { return c.word == s; } );
if ( it == std::end( word_count ) )
{
word_count.push_back( s );
}
else
{
++it->n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
first: 2
second: 1
Pay attention to that the definition of the struct counter is redundant. You could use instead the standard class std::pair. Here you are.
#include <iostream>
#include <string>
#include <utility>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
std::list<std::pair<std::string, size_t>> list_count( const std::vector<std::string> &text )
{
std::list<std::pair<std::string, size_t>> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &p ) { return p.first == s; } );
if ( it == std::end( word_count ) )
{
word_count.emplace_back( s, 1 );
}
else
{
++it->second;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
If to use std::map then the function will look very simple.
#include <iostream>
#include <string>
#include <vector>
#include <map>
std::map<std::string, size_t> list_count( const std::vector<std::string> &text )
{
std::map<std::string, size_t> word_count;
for ( const auto &s : text )
{
++word_count[s];
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
Using of the list will be efficient only in the case when the vector of strings is sorted.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <list>
#include <vector>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
if ( word_count.empty() || word_count.back().word != s )
{
word_count.push_back( s );
}
else
{
++word_count.back().n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "A", "B", "B", "C", "C", "C", "D", "D", "E" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
A: 1
B: 2
C: 3
D: 2
E: 1
how can I store the values returned from a function to a string as comma seperated values. Can anyone help me..?
const myVector &vecList = obj.get_List();
vector<myNumVector *>::const_iterator iter;
for (iter= vecList.begin(); iter!= vecList.end(); iter++)
{
string myNum = (*iter)->get_myNum();
string myNumList = ?
//myNumList should be = drt123,ret34,dfghgd234.... if these are the return values
} //can we achive this by use of some sting functions..?
As can be seen from the links I posted, there are lots of ways to do this. Here is, I believe, the simplest:
#include <vector>
using std::vector;
#include <string>
using std::string;
#include <boost/assign/list_of.hpp>
using boost::assign::list_of;
namespace ba = boost::assign;
vector<string> victor = list_of
("Clarence Oveur")
("Roger Murdock")
("Victor Basta");
int main() {
string result;
for(vector<string>::iterator it = victor.begin();
it != victor.end();
++it) {
if(it != victor.begin()) {
result += ", ";
}
result += *it;
}
cout << result << "\n";
}
EDIT: To translate directly to OP's question:
const myVector &vecList = obj.get_List();
vector<myNumVector *>::const_iterator iter;
string myNumlist;
for (iter= vecList.begin(); iter!= vecList.end(); iter++)
{
string myNum = (*iter)->get_myNum();
if(iter!=vecList.begin()) {
nyNumList += ",";
}
myNumList += myNum;
}
EDIT: Simplified by removing bool first from previous solution.
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
int main () {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
std::stringstream list;
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(list, ","));
std::cout << list.str();
return 0;
}
Outputs: 1,2,3,4,
more modern approach, also solving the trailing ","
#include <string>
#include <numeric>
#include <iostream>
int main() {
const auto v = {1, 2, 3, 4};
const auto list = std::accumulate(begin(v), end(v), std::string{}, [](const std::string& so_far, const auto& next) {
return so_far + (so_far.empty() ? "" : ", ") + std::to_string(next);
});
std::cout << list;
return 0;
}
Yes, this can be achieved using string functions, along with a handful other methods.
Given a string myNumList defined outside the loop, you could simply
myNumList += "," + myNum;
although that would add an extraneous comma in the beinning, so check if iter is pointing there first:
if(iter != vecList.begin())
myNumList += ',';
myNumList += myNum;