I am keeping reading Stanford tutorials and currently solving tasks on STL. The task is to write a function which accepts a map with names of films and their rankings. This function should return a set container with top 3 films according to critics review. Here is my solution:
#include <iostream>
#include <set>
#include <map>
#include <numeric>
#include <iterator>
#include <string>
using namespace std;
struct compare {
bool operator() (const double& a, const double& b) const {
return (a > b);
}
};
set <string> list(map <double, string, compare>& films) {
set <string> critics;
map<double, string, compare> ::iterator it = films.begin();
if (films.size() <= 3) {
critics.insert(films.begin()->second, films.end()->second);
}
else {
for (int i = 0; i < 3; ++i, ++it){
critics.insert(it->second);
}
};
return critics;
}
int main() {
map <double, string, compare> films;
films[5.0] = "a";
films[8.0] = "b";
films[10.0] = "c";
films[7.4] = "d";
set <string> critics = list(films);
copy(critics.begin(), critics.end(), ostream_iterator <string>(cout, " "));
cin.get();
}
Unfortunately, it keeps on throwing out an error:
error C2675: unary '++' : 'std::string' does not define this operator or a conversion to a type acceptable to the predefined operator
I have read MSDN documentation on the error but as I am new to this could not understand the meaning of the problem. Could you, please, hint me at that?
This statement
critics.insert(films.begin()->second, films.end()->second);
is invalid. The compiler consideres arguments films.begin()->second and films.end()->second of type std::string as a pair of iterators and tries to apply operator ++. Of course this results in an error.
You should use standard algorithm std::transform with std::insert_iterator that to copy strings from the map to the set.
Here is a demonstrative program that shows the approach
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <algorithm>
#include <iterator>
#include <functional>
int main()
{
std::map<double, std::string, std::greater<double>> m =
{
{ 2.2, "B" }, { 1.1, "A" }, { 4.4, "D" }, { 5.5, "E" }, { 3.3, "C" }
};
for ( const auto &p : m )
{
std::cout << p.first << '\t' << p.second << std::endl;
}
std::set<std::string> s;
std::transform( m.begin(), std::next( m.begin(), 3 ),
std::inserter( s, s.end() ),
[]( const auto &p ) { return p.second; } );
for ( const auto &t : s ) std::cout << t << ' ';
std::cout << std::endl;
return 0;
}
The program output is
5.5 E
4.4 D
3.3 C
2.2 B
1.1 A
C D E
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 << ' ';
}
I have several sets of the same type:
std::set< TDate> spieltagDatum;
I would like to include all of them into a multimap of this type:
std::multimap<TDate, int> ereignis;
Is there an elegant possibility (perhaps with a lambda related function?) to include all members of ONE set into the multimap above not using the iterator mechanism? (The multimap pairs should be enriched with the INT parameter during insert).
I can suggest instead of iterators to use simplified for loop with auto like below.
I used integer TDate just for example, also instead of 123 in my code you may put any function for filling in values of multimap.
Try it online!
#include <map>
#include <set>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::multimap<TDate, int> ereignis;
for (auto & e: spieltagDatum)
ereignis.emplace(e, 123);
}
What do you mean by "not using the iterator mechanism"? (Don't use iterators at your own peril)
As you describe, what you do is to 1) transform (by enrich) and 2) insert, so the answer is std::tranform + std::insert.
#include <algorithm> // transform
#include <cassert>
#include <map>
#include <set>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::set<TDate> ...;
std::multimap<TDate, int> ereignis;
auto enrich = [](auto e){return std::make_pair(e, 123);};
std::transform(
begin(spieltagDatum), end(spieltagDatum),
std::inserter(ereignis, end(ereignis)),
enrich
);
... // repeat for other sets if necessary
assert( ereignis.find(5) != ereignis.end() );
assert( ereignis.find(5)->second == 123 );
}
https://godbolt.org/z/zzYbKK83d
A more declarative option using libraries, based on #prehistoricpenguin answer is:
(IMO it is worth mainly in C++17, where so many of the templates parameters are not really necessary)
#include <cassert>
#include <map>
#include <set>
#include <boost/iterator/transform_iterator.hpp>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
auto enriched = [](auto it){
return boost::transform_iterator(it, [](auto e){return std::pair(e, 123);});
};
std::multimap ereignis(
enriched(begin(spieltagDatum)),
enriched(end (spieltagDatum))
);
assert( ereignis.find(5) != ereignis.end() );
assert( ereignis.find(5)->second == 123 );
}
https://godbolt.org/z/6ajssjjjP
One possible answer is to write a convert iterator class, then we use the iterator to constructor the multimap instance.
#include <iostream>
#include <iterator>
#include <map>
#include <set>
template <typename KeyT, typename ValT>
class ConvertIter
: public std::iterator<std::forward_iterator_tag, std::pair<KeyT, ValT>> {
using SetIter = typename std::set<KeyT>::iterator;
public:
ConvertIter(SetIter itr, ValT v = ValT{}) : _itr(itr), _val(v) {}
bool operator==(const ConvertIter& other) { return other._itr == _itr; }
bool operator!=(const ConvertIter& other) { return other._itr != _itr; }
std::pair<KeyT, ValT> operator*() const {
return {*_itr, _val};
}
ConvertIter& operator++() {
++_itr;
return *this;
}
ConvertIter& operator++(int) {
++_itr;
return *this;
}
private:
SetIter _itr;
ValT _val;
};
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::multimap<TDate, int> ereignis(
ConvertIter<TDate, int>(spieltagDatum.begin(), 123),
ConvertIter<TDate, int>(spieltagDatum.end()));
for (auto [date, val] : ereignis) {
std::cout << "[" << date << "," << val << "]" << std::endl;
}
return 0;
}
Demo:
https://godbolt.org/z/cr98f15jq
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 have a vector of elements of type uint8 which I am copying into one std::array. After some operations again I need to copy the elements from std::array to a vector.Please find the code below
/******************************************************************************
Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.
*******************************************************************************/
#include <iostream>
#include <vector>
#include <array>
#include <cstdint>
#include <limits>
#include <algorithm>
std::array<std::uint32_t, 5> elem_arr ;
void copy_from_vector(std::vector<std::uint32_t> elem_vec)
{
std::copy(elem_vec.begin(),elem_vec.end(),elem_arr.begin());
}
std::vector<std::uint32_t> copy_from_array()
{
std::vector<std::uint32_t> ele_vec_copy {elem_arr.begin(),elem_arr.end()};
return ele_vec_copy;
}
int main()
{
std::vector<std::uint32_t> ele_vec {1,2,3};
copy_from_vector(ele_vec);
auto ele_vec_copy = copy_from_array();
for(auto ele : ele_vec_copy)
std::cout << ele << std::endl;
return 0;
}
output:
======
1
2
3
0
0
Now the problem is my array size is 5 but vector having only 3 elements. when I copy the elements from the array to a new vector I am getting extra 2 zeros. How can I get the output as below
output:
======
1
2
3
You could copy by size of vector.
#include <iostream>
#include <vector>
#include <array>
#include <cstdint>
#include <limits>
#include <algorithm>
int main()
{
std::array<std::uint32_t, 5> elem_arr ;
std::vector<std::uint32_t> ele_vec {1,2,3};
std::copy(ele_vec.begin(),ele_vec.end(),elem_arr.begin());
std::vector<std::uint32_t> ele_vec_copy { elem_arr.begin() , elem_arr.begin() + ele_vec.size() };
for(auto ele : ele_vec_copy)
std::cout << ele << std::endl;
return 0;
}
run online
After you edited the question
It is not possible to know how many elements used/set in std::array but this feature could be mimic by choosing a unique value which represents the element is null or not set like nullptr. But it comes with a constraint, you must not use the unique value at somewhere else in the array.
So the possible implementation is :
#include <iostream>
#include <vector>
#include <array>
#include <cstdint>
#include <limits>
#include <algorithm>
#include <limits>
std::array<std::uint32_t, 5> elem_arr;
void copy_from_vector(std::vector<std::uint32_t> elem_vec)
{
std::copy(elem_vec.begin(),elem_vec.end(),elem_arr.begin());
}
std::vector<std::uint32_t> copy_from_array()
{
std::vector<std::uint32_t> ele_vec_copy;
std::copy_if( elem_arr.begin() , elem_arr.end() ,
std::back_inserter( ele_vec_copy ) ,
[]( std::uint32_t val ) {
return val != std::numeric_limits<std::uint32_t>::max();
} );
return ele_vec_copy;
}
int main()
{
elem_arr.fill( std::numeric_limits<std::uint32_t>::max() );
std::vector<std::uint32_t> ele_vec {1,2,3};
copy_from_vector(ele_vec);
auto ele_vec_copy = copy_from_array();
for(auto ele : ele_vec_copy)
std::cout << ele << std::endl;
return 0;
}
run online
Or store the elements as std::optional and don't copy null elements.
Possible implementation is :
#include <iostream>
#include <vector>
#include <array>
#include <cstdint>
#include <limits>
#include <algorithm>
#include <limits>
#include <optional>
std::array<std::optional<std::uint32_t>, 5> elem_arr;
void copy_from_vector(std::vector<std::uint32_t> elem_vec)
{
std::transform( elem_vec.begin() , elem_vec.end() ,
elem_arr.begin() ,
[]( auto val ) { return std::optional<std::uint32_t>{ val }; }
);
}
std::vector<std::uint32_t> copy_from_array()
{
std::vector<std::uint32_t> ele_vec_copy;
std::vector<std::optional<std::uint32_t>> non_null_elements;
std::copy_if( elem_arr.begin() , elem_arr.end() ,
std::back_inserter( non_null_elements ) ,
[]( auto val ) {
return val;
} );
std::transform( non_null_elements.begin(), non_null_elements.end() ,
std::back_inserter( ele_vec_copy ) ,
[]( auto val ) { return *val; }
);
return ele_vec_copy;
}
int main()
{
std::vector<std::uint32_t> ele_vec {1,2,3};
copy_from_vector(ele_vec);
auto ele_vec_copy = copy_from_array();
for(auto ele : ele_vec_copy)
std::cout << ele << std::endl;
return 0;
}
run online
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