How to pass a string iterator to other functions - c++

I have this code which tries calling count with a std::string iterator but it fails with a compilation error.
#include <iostream>
#include <algorithm>
int count(std::string::iterator iter, char ch)
{
int n = 0;
while (*iter == ch) {
n++;
iter++;
}
return n;
}
void search(std::string const& str, char ch)
{
auto iter = std::find(str.begin(), str.end(), ch);
if (iter != str.end()) {
int n = count(iter, ch);
std::cout << n << " consecutive '" << ch << "' found";
} else {
std::cout << "no char found";
}
std::cout << std::endl;
}
int main()
{
std::string line;
while (std::getline(std::cin, line)) {
search(line, 'a');
}
return 0;
}
The error I'm getting:
00.cpp: In function 'void search(const string&, char)':
00.cpp:19:31: error: no matching function for call to 'count(__gnu_cxx::__normal_iterator >&, char&)'
I found out that if I change the full type std::string::iterator to auto in function count and let the compiler infers the type it will work as expected (or at least I think it works). Why?

str is marked as const, then the iterator taken from it, i.e. the type of iter is std::string::const_iterator, which can't be converted to std::string::iterator implicitly and passed to count.
Since count won't modify element pointed by iter, you can make count taking const_iterator too.
int count(std::string::const_iterator iter, char ch)

Related

Iterating over list to find occurrences of element. Issue with code

Does anyone know what's wrong with this code? I'm getting the following compilation error. The goal is to find the occurrences of a string "p" and I took the idea from Stroustrup P57. My assumption is that I could just increment the iterator to find additional occurrences, but that is not working. Thanks
find.cc: In function ‘int main(int, char**)’:
find.cc:34:16: error: no match for ‘operator+’ (operand types are ‘LI {aka std::_List_const_iterator<Ent>}’ and ‘int’)
i = find(i + 1, l.end(), e1);
#include <iostream>
#include <algorithm>
#include <list>
#include <string>
using namespace std;
struct Ent {
string name;
Ent(const string& name) : name(name) { }
bool operator== (const Ent& right) const {
return name == right.name;
}
};
int main(int argc, char *argv[])
{
list<Ent> l;
for (char c = 'a'; c <= 'z'; c++) {
Ent e(string(1, c));
l.push_back(e);
}
Ent e1("p");
typedef list<Ent>::const_iterator LI;
LI i = find(l.begin(), l.end(), e1);
int n = 0;
while (i != l.end()) {
++n;
i = find(i + 1, l.end(), e1);
}
cout << "find(" << e1.name << ") = " << n << endl;
return 0;
}
Lists iterators are bidirectional iterators but not randomaccess iterators. Hence they have no operator+, but only a operator++. You can write
++i;
i = find(i , l.end(), e1);
instead.

C++ Find and save duplicates in vector

I have a custom vector of my user defined type vector
First vector gets filled with elements through stdin, then i sort it and try to find duplicates in it and save them
i've managed to find all unique elements, but i need to find and get a vector of duplicates
I need a hint or a simple solution for this problem
here's my code below:
Agressor.h
#ifndef Agressor_h
#define Agressor_h
#include <string>
#include <vector>
using namespace std;
class Agressor{
public:
/*const char**/ string traderIdentifier;
/*const char**/ string side;
int quantity;
int price;
vector<Agressor> v;
void display(){
cout << traderIdentifier << " " << side << " " << quantity << " " << price << endl;
}
explicit Agressor(){
}
~Agressor(){
}
friend ostream &operator<<(ostream& stream, const Agressor& item);
const friend bool operator > (const Agressor &a1, const Agressor &a2);
// const friend bool operator == (const Agressor &a1, const Agressor &a2);
/* vector<Agressor>& operator[](int i ){
return v[i];
}*/
};
ostream &operator<<(ostream& stream, const Agressor& item) {
string side = "";
if(item.side == "B"){
side = '+';
}else{
if(item.side == "S"){
side = "-";
}
}
stream << item.traderIdentifier << side << item.quantity << "#" << item.price << "\n";
return stream;
}
const bool operator == (const Agressor &a1, const Agressor &a2){
bool isEqual = false;
if((a1.price*a1.quantity == a2.price*a2.quantity) && (a1.traderIdentifier == a2.traderIdentifier) && (a1.side == a2.side)){
isEqual = true;
}
return(isEqual);
}
const bool operator > (const Agressor &a1, const Agressor &a2){
bool isGreater = false;
if(a1.price*a1.quantity > a2.price*a2.quantity){
isGreater = true;
}
return(isGreater);
}
#endif /* Agressor_h */
main.cpp
#include <iostream>
#include "Agressor.h"
#include <sstream>
using namespace std;
vector<string> &split(const string &s, char delim, vector<string> &elems)
{
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
vector<string> split(const string &s, char delim)
{
vector<string> elems;
split(s, delim, elems);
return elems;
}
bool equal_comp(const Agressor& a1, const Agressor& a2){
if((a1.price*a1.quantity == a2.price*a2.quantity) && (a1.traderIdentifier == a2.traderIdentifier) && (a1.side == a2.side)){
return true;
}
return false;
}
int main(int argc, const char * argv[]) {
Agressor agr;
while (true) {
std::string sText;
cout << "enter query:" << endl;
std::getline(std::cin, sText);
if(sText == "q"){
cout << "Program terminated by user" << endl;
break;
}else{
std::vector<std::string> sWords = split(sText, ' ');
agr.traderIdentifier = sWords[0];
agr.side = sWords[1];
agr.quantity = stoi(sWords[2]);
agr.price = stoi(sWords[3]);
agr.v.push_back(agr);
vector<Agressor>::iterator it;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
//unique(agr.v.begin(), agr.v.end(), equal_comp);
for (vector<Agressor>::const_iterator i = agr.v.begin(); i != agr.v.end(); ++i)
cout << *i << ' ';
}
}
cout << "here we go..." << endl;
vector<Agressor>::iterator it;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
//it = unique(agr.v.begin(),agr.v.end(), equal_comp);
//agr.v.resize( distance(agr.v.begin(),it) );
agr.v.erase(unique(agr.v.begin(),agr.v.end(), equal_comp), agr.v.end());
copy(agr.v.begin(), agr.v.end(), ostream_iterator<Agressor>(cout, "\n"));
return 0;
}
You might use something like:
template <typename T>
std::vector<T> get_duplicates(const std::vector<T>& v)
{
// expect sorted vector
auto it = v.begin();
auto end = v.end();
std::vector<T> res;
while (it != end) {
it = std::adjacent_find(it, end);
if (it != end) {
++it;
res.push_back(*it);
}
}
return res;
}
std::unique overwrites duplicate values with later non-duplicate values. You can implement a similar algorithm that moves the values to somewhere.
template<class ForwardIt, class OutputIt, class BinaryPredicate>
ForwardIt unique_retain(ForwardIt first, ForwardIt last, OutputIt d_first, BinaryPredicate p)
{
if (first == last)
return last;
ForwardIt result = first;
while (++first != last) {
if (!p(*result, *first) && ++result != first) {
*d_first++ = std::move(*result);
*result = std::move(*first);
}
}
return ++result;
}
(adapted from this possible implementation of std::unique)
You would then use it like
vector<Agressor> dups;
sort(agr.v.begin(), agr.v.end(), greater<Agressor>());
auto it = unique_retain(agr.v.begin(),agr.v.end(), std::back_inserter(dups), equal_comp);
agr.v.erase(it, agr.v.end());

C++Primer 5th in p.343. that no match for c.insert(p,b,e)

There is a problem that I can't understand.
Look the picture as follows:
#include <iostream>
#include<string>
using namespace std;
// use the string rep to replace the value in string str
void Replace(string& str,const char& value,const string& rep)
{
auto iter = str.begin();
while(iter != str.end())
{
if(*iter == value)
{
iter = str.erase(iter);
iter = str.insert(iter,rep.begin(),rep.end());
iter += rep.size();
continue;
}
++iter;
}
}
int main()
{
string str = "Welcome to Stack Overflow";
Replace(str,' ',"****");
cout<<str<<endl;
return 0;
}
GCC 4.9(C++11)
Compiler messages:
main.cpp: In function 'void Replace(std::string&, const char&, const string&)':
main.cpp:14:18: error: no match for 'operator=' (operand types are '__gnu_cxx::__normal_iterator<char*, std::basic_string<char> >' and 'void')
iter = str.insert(iter,rep.begin(),rep.end());
but..I can't find the function drawed above the picture in cppreference.
http://en.cppreference.com/w/cpp/string/basic_string/insert
so:
Please tell me, Did I say wrong??
and then how I to fit it ?

Count the number of unique words (case does not matter for this count)

Hey so I'm having trouble figuring out the code to count the number of unique words. My thought process in terms of psudeocode was first making a vector so something like vector<string> unique_word_list;Then I would get the program to read each line so I would have something likewhile(getline(fin,line)). The hard part for me is coming up with the code where I check the vector(array) to see if the string is already in there. If it's in there I just increase the word count(simple enough) but if its not in there then I just add a new element to the vector. I would really appreciate if someone could help me out here. I feel like this is not hard but for some reason I can't think of the code for comparing the string with whats inside of the array and determining if its a unique word or not.
Don't use a vector - use a container that maintains uniqueness, like std::set or std::unordered_set. Just convert the string into lower case (using std::tolower) before you add it:
std::set<std::string> words;
std::string next;
while (file >> next) {
std::transform(next.begin(), next.end(), next.begin(), std::tolower);
words.insert(next);
}
std::cout << "We have " << words.size() << " unique words.\n"
Cannot help myself writing an answer that makes use of C++ beautiful library. I'd do it like this, with a std::set:
#include <algorithm>
#include <cctype>
#include <string>
#include <set>
#include <fstream>
#include <iterator>
#include <iostream>
int main()
{
std::ifstream ifile("test.txt");
std::istream_iterator<std::string> it{ifile};
std::set<std::string> uniques;
std::transform(it, {}, std::inserter(uniques, uniques.begin()),
[](std::string str) // make it lower case, so case doesn't matter anymore
{
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
return str;
});
// display the unique elements
for(auto&& elem: uniques)
std::cout << elem << " ";
// display the size:
std::cout << std::endl << uniques.size();
}
You can also define a new string type in which you change the char_traits so the comparison becomes case-insensitive. This is the code you'd need (much more lengthy than before, but you may end up reusing it), the char_traits overload is copy/pasted from cppreference.com:
#include <algorithm>
#include <cctype>
#include <string>
#include <set>
#include <fstream>
#include <iterator>
#include <iostream>
struct ci_char_traits : public std::char_traits<char> {
static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
static bool lt(char c1, char c2) { return toupper(c1) < toupper(c2); }
static int compare(const char* s1, const char* s2, size_t n) {
while ( n-- != 0 ) {
if ( toupper(*s1) < toupper(*s2) ) return -1;
if ( toupper(*s1) > toupper(*s2) ) return 1;
++s1; ++s2;
}
return 0;
}
static const char* find(const char* s, int n, char a) {
while ( n-- > 0 && toupper(*s) != toupper(a) ) {
++s;
}
return s;
}
};
using ci_string = std::basic_string<char, ci_char_traits>;
// need to overwrite the insertion and extraction operators,
// otherwise cannot use them with our new type
std::ostream& operator<<(std::ostream& os, const ci_string& str) {
return os.write(str.data(), str.size());
}
std::istream& operator>>(std::istream& os, ci_string& str) {
std::string tmp;
os >> tmp;
str.assign(tmp.data(), tmp.size());
return os;
}
int main()
{
std::ifstream ifile("test.txt");
std::istream_iterator<ci_string> it{ifile};
std::set<ci_string> uniques(it, {}); // that's it
// display the unique elements
for (auto && elem : uniques)
std::cout << elem << " ";
// display the size:
std::cout << std::endl << uniques.size();
}

Convert a set to a vector of vector

I'm new to C++ and I'm trying to convert unordered_set<string> to vector<vector<int>>
The set contains ("1,2,2","1","1,2","2","2,2"), and each element is a string.
I would like to output a vector<vector<int>> containing
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
So how would I parse each element (string) and make it looks like the above scheme?
#define SSTR( x ) dynamic_cast< std::ostringstream & >( \
( std::ostringstream() << std::dec << x ) ).str()
vector<vector<int> > subsetsWithDup(const vector<int> &num) {
unordered_set<string> result;
for (int i = 0; i < num.size(); i++)
{
result.insert(SSTR(num[i]));
for (int j = i+1; j < num.size(); j++)
{
string d = SSTR(num[i]) + "," +SSTR(num[j]);
result.insert(d);
}
}
string lastString= "";
for (int i = 0; i < num.size(); i++)
{
if ( i == num.size() -1)
{
lastString+= SSTR(num[i]);
}
else
{
lastString+= SSTR(num[i])+",";
}
}
result.insert(lastString);
// convert result back to vector<vector<int>>
return result;
}
If you don't need to validate your strings you can do a transform to construct the vector<vector<int>>:
set<string> foo{ "1,2,2", "1", "1,2", "2", "2,2" };
vector<vector<int>> bar(foo.size());
transform(foo.begin(), foo.end(), bar.begin(), [](const string& i){
vector<int> result;
auto it = const_cast<char*>(i.c_str());
for (result.push_back(static_cast<int>(strtol(it, &it, 10)));
it < i.c_str() + i.size();
result.push_back(static_cast<int>(strtol(++it, &it, 10))));
return result;
});
The lambda in the transform will step through the string:
Start it at the beginning of the string
Using strtol to extract each number
Pushing each number into result
Stepping over each ',' with ++it
Return the constructed result
Prerequisites: This lambda assumes that your input set doesn't contain:
An empty string
A string that begins or ends with a comma (as in "1,2,")
A string that has consecutive commas (as in "1,,2")
That the string contains only contains digits and commas
Note: As a general rule a const_cast is bad so I wanted to comment on why I'm making one. Note that *it is never written to, only it is written to. So this is not violating the const-ness of const string& i. An alternative to strtol that doesn't require a const_cast is stoi, but until we get string_view from C++14 we'll need to construct a new string at each comma, so that's dreadfully inefficient.
One of the possible solution can be:
vector<string> split (string str, string seq) {
vector<string> ret {};
size_t pos {};
while ((pos = str.find (seq)) != string::npos) {
ret.push_back (str.substr (0, pos));
str = str.substr (pos+seq.size ());
}
ret.push_back (str);
return ret;
}
vector<int> to_vec_int (vector<string>&& vec) {
vector<int> ret {};
for (const auto& v : vec) {
ret.push_back (stoi (v));
}
return ret;
}
int main () {
unordered_set<string> st {"1,2,2","1","1,2","2","2,2"};
vector<vector<int>> vec {};
for (const auto& s : st) {
vec.push_back (to_vec_int (split (s, ",")));
}
for (const auto& v : vec) {
for (const auto& s : v) {
cout << s << " ";
}
cout << endl;
}
return 0;
}
Since your elements are delimited by comma, we can split them using split () function and we get vector of strings. This vector has to be converted to vector of int, which is the reason of existence of to_vec_int ().
Here is another possible solution using istringstream to find the commas:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
#include <sstream>
void main()
{
using namespace std;
unordered_set<string> strSet;
strSet.insert("1,2,2");
strSet.insert("1");
strSet.insert("1,2");
strSet.insert("2");
strSet.insert("2,2");
vector<int> nums;
vector<vector<int>> arr_of_nums;
for (const auto &str : strSet) {
istringstream strStream(str);
string strToInt;
while (getline(strStream, strToInt, ',')) {
nums.push_back(stoi(strToInt));
}
arr_of_nums.push_back(nums);
nums.clear();
}
for(const auto &nums : arr_of_nums) {
for (const auto &num : nums) {
cout << num << ",";
}
cout << endl;
}
}