C++ join function that works on strings and chars - c++

I've written a join() function that works on iterators, with this signature:
template <typename Iter>
string join(Iter begin, Iter end, string sep = ", ");
I'd like to work for both iterators to strings and iterators to chars. But it uses string::append(*iter), which doesn't work for chars. How can I specialize it?
Edit: Here is my implementation. It works great for collections of strings, but not collections of chars.
template <typename Iter>
string join(Iter begin, Iter end, string sep = ", ") {
string result;
for (Iter i = begin; i != end; ++i) {
if (result.size() > 0) {
result.append(sep);
}
result.append(*i); // <-- fails if called with a collection of chars
}
return result;
}
#HolyBlackCat's suggestion of += makes it work!

As #HolyBlackCat mentioned, you should be able to use the += operator here. In general you could create concepts though to create overloads for the relevant functionality:
template<class Iterator>
concept StringIterator = requires(Iterator i)
{
{ *i } -> std::convertible_to<std::string const&>;
};
template<class Iterator>
concept CharIterator = requires(Iterator i)
{
{ *i } -> std::convertible_to<char>;
};
template<StringIterator Iter>
void Append(std::string& target, Iter pos)
{
target.append(*pos);
}
template<CharIterator Iter>
void Append(std::string& target, Iter pos)
{
target.push_back(*pos);
}
template<class Iter>
std::string join(Iter begin, Iter end, std::string const& sep = ", ")
{
std::string result;
if (begin != end)
{
Append(result, begin);
for (++begin; begin != end; ++begin)
{
result += sep;
Append(result, begin);
}
}
return result;
}
int main() {
std::vector<std::string> strings{ "foo", "bar", "baz"};
std::string s = "Hello World";
std::cout << join(strings.begin(), strings.end()) << '\n';
std::cout << join(s.begin(), s.end()) << '\n';
}

Related

Is there a way to use the erase-remove idiom in concert with other looping conditions?

I have a function that, given a block of text, should remove all punctuation characters, and make all letters lowercase, and eventually, should shift them according to a mono alphabetic cipher. The code below works:
class Cipher {
public:
string keyword;
string decipheredText;
deque<string> encipheredAlphabet;
static bool is_punctuation (char c) {
return c == '.' || c == ',' || c == '!' || c == '\''|| c == '?' || c
== ' ';
}
string encipher(string text) {
Alphabet a;
encipheredAlphabet = a.cipherLetters(keyword);
text.erase( remove_if(text.begin(), text.end(), is_punctuation),
text.end() );
string::iterator it;
for (it = text.begin(); it != text.end(); it++) {
*it = tolower(*it);
// encipher text according to shift
}
return text;
}
};
The problem is, it currently makes two passes over the string, one to remove the punctuation, and one to do all the other stuff. This seems inefficient, since it seems like all the transformations could be accomplished in one pass through the string somehow. Is there a clean way to incorporate the erase-remove idiom with other loop conditions?
With range-v3, you might create (lazy) view:
return text | ranges::view::filter([](char c){ return !is_punctuation(c); })
| ranges::view::transform([](char c) -> char { return to_lower(c); });
You could do it by using std::accumulate and an iterator as init value that insert into an output std::string
auto filter = [](auto pred) {
return [=](auto map) {
auto accumulator = [=](auto it, auto c) {
if (pred(c)) {
*it = map(c);
}
return ++it;
};
return accumulator;
};
};
auto accumulator = filter(std::not_fn(is_punctuation))
([](auto c) {
return std::tolower(c);
});
std::string in = "insIsjs.|s!js";
std::string out;
std::accumulate(std::begin(in), std::end(in), std::back_inserter(out), accumulator);
See demo
Copy and/or modify characters, then truncate the string :
string encipher(string text)
{
auto it = text.begin(),
jt = it;
for (; it != text.end(); it++)
{
if (!is_punctuation(*it))
{
*jt = tolower(*it);
++jt;
}
}
text.erase(jt, it);
return text;
}
If you don't want to do two loops because you've measured and found that it's slower, write a custom algorithm:
template <typename Iter, typename OutIter>
OutIter lowercased_without_punctuation(Iter begin, Iter end, OutIter out) {
while (begin != end) {
// Ignoring things like std::move_iterator for brevity.
if (!is_punctuation(*begin)) {
*out = tolower(*begin);
++out;
}
// Use `++iter` rather than `iter++` when possible
++begin;
}
return out;
}
// ...
string encipher(string text) {
Alphabet a;
encipheredAlphabet = a.cipherLetters(keyword);
text.erase(
lowercased_without_punctuation(text.begin(), text.end(), text.begin()),
text.end());
return text;
}
If you think about it some more, lowercased_without_punctuation is actually a special-case of a more general algorithm which might be called transform_if (relevant Q&A):
template <typename Iter, typename OutIter, typename Pred, typename Transf>
OutIter transform_if(Iter begin, Iter end, OutIter out, Pred p, Transf t) {
while (begin != end) {
if (p(*begin)) {
*out = t(*begin);
++out;
}
++begin;
}
return out;
}
// ...
string encipher(string text) {
Alphabet a;
encipheredAlphabet = a.cipherLetters(keyword);
text.erase(
transform_if(text.begin(), text.end(), text.begin(),
[](char c) { return !is_punctuation(c); },
[](char c) { return tolower(c); }),
text.end());
return text;
}

Iterating over a container bidirectionally

Is there a better way than the below code, to iterate over a container in either direction, using the same iterators?
#include <iostream>
#include <map>
int main()
{
const bool descend = false;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
std::map<int, int>::iterator startIter = descend ? --(mapp.end()) : mapp.begin();
std::map<int, int>::iterator endIter = descend ? --(mapp.begin()) : mapp.end();
while (startIter != endIter)
{
std::cout << startIter->first << std::endl;
descend ? --startIter : ++startIter;
}
}
Your code is invalid as this statement --(mapp.begin()) leads to UB. I would write a thin wrapper:
template<class Iter, class F>
void apply( Iter begin, Iter end, F f, bool forward )
{
while( begin != end )
f( forward ? *begin++ : *--end );
}
live example
or just simply rewrite your loop into:
auto begin = mapp.begin();
auto end = mapp.end();
while ( begin != end)
{
const auto &p = forward ? *begin++ : *--end;
std::cout << p.first << std::endl;
}
Is there a better way than the below code, to iterate over a container
in either direction, using the same iterators?
Yes. Use std::map::reverse_iterator. It will be a better way than the code you posted, but that will not be using the same iterators anymore, which was one of your requirements.
However, this will be less error-prone than the code you have written. In addition to that, you do not need to re-invent the wheel, if that is already in C++.
See output here
#include <iostream>
#include <map>
template<typename Iterator>
void print(const Iterator Begin, const Iterator End)
{
for(Iterator iter = Begin; iter != End; ++iter)
std::cout << iter->first << "\n";
}
int main()
{
const bool descend = true;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
descend ?
print(mapp.crbegin(), mapp.crend()):
print(mapp.cbegin(), mapp.cend());
return 0;
}
The image from cppreference.com will explain graphically, how does it work.
Write self-documenting code and it becomes simple. Break that loop out into its own function and call it with the appropriate iterators.
This is why we have "reverse iterators" they can be used to go backwards through a container by using the normal forward semantics.
#include <iostream>
#include <map>
template<typename I>
void printMapContainer(I begin, I end)
{
for (;begin != end; ++begin)
{
std::cout << begin->first << "\n";
}
}
int main()
{
const bool descend = false;
std::map<int, int> mapp;
mapp[1] = 1;
mapp[2] = 2;
mapp[3] = 3;
mapp[4] = 4;
if (descend) {
printMapContainer(mapp.rbegin(), mapp.rend());
}
else {
printMapContainer(mapp.begin(), mapp.end());
}
}

[C++]Cut up a string into strings separated by anything other than symbols given in another string?

EDIT: This is not a duplicate! The question in the link given is different. I'm not separating by spaces, I'm separating by anything other than characters in a given string. Therefor, the answers in the other thread don't work.
EDIT2: Please note that I'm not given the delimiters to separate by, I need to separate by anything OTHER THAN notOf.
In my program, that means cut up string named word into strings in a vector named words, which consist of and are separated by anything other than notOf. So notOf is sort of the opposite of a delimited in getline.
For example, "&otherwise[taken=(and)-redacted-" should become "otherwise", "taken", "and" and "redacted".
I've tried writing and came up with what you see below, but it doesn't work and is also, I've been told, considerably ugly. Use your judgement whether it is worth fixing or writing anew.
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
using namespace std;
int main()
{
string notOf = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789$'\"";
string word = "&otherwise[taken=(and)-redacted-";
stringstream in(word);
vector<string> words;
if (word.find_first_not_of(notOf) == string::npos)
words.push_back(word);
else while (word.find_first_not_of(notOf) != string::npos)
{
int index = word.find_first_not_of(notOf);
string tok;
getline(in, tok, word[index]);
if (index != 0)
words.push_back(tok);
word = word.substr(index);
}
for (vector<string>::iterator it = words.begin(); it != words.end(); ++it)
cout << *it << endl;
system("pause");
return 0;
}
You can try this :
vector<string>split(string str,string Separator)
{
vector<string>answer;
string temp;
int len=str.size();
for(int i=0;i<len;i++)
{
bool isSeparator=false;
for(int j=0;j<Separator.length();j++)
{
if(str[i]==Separator[j])
isSeparator=true;
}
if(!isSeparator)
{
temp+=str[i];
continue;
}
if(temp!="")
answer.push_back(temp);
temp="";
}
if(temp!="")
answer.push_back(temp);
return answer;
}
You just need to specify the separators in Separator string. This function will return the separated string. Here separating would take place where one of the Separator found.
Edit :
If you want to partition with respect to notOf (that is other than notOf) , you can do the following :
vector<string>split(string str,string Separator)
{
vector<string>answer;string temp;
int len=str.size();
for(int i=0;i<len;i++)
{
bool isSeparator=true;
for(int j=0;j<Separator.length();j++)
{
if(str[i]==Separator[j])
isSeparator=false;
}
if(!isSeparator)
{
temp+=str[i];
continue;
}
if(temp!="")
answer.push_back(temp);
temp="";
}
if(temp!="")
answer.push_back(temp);
return answer;
}
int main()
{
string notOf = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789$'\"";
string word = "&otherwise[taken=(and)-redacted-";
vector<string>v=split(word,notOf);
for(int i=0;i<v.size();i++)
cout<<v[i]<<"\n";
return 0;
}
Hope that helps :)
I was thinking about the most elegant way of doing this if you were given a range of delimiters or valid characters, and using nothing more than the standard library.
Here are my thoughts:
to split words into a string vector by a sequence of delimiters:
template<class Container>
std::vector<std::string> split_by_delimiters(const std::string& input, const Container& delimiters)
{
std::vector<std::string> result;
for (auto current = begin(input) ; current != end(input) ; )
{
auto first = find_if(current, end(input), not_in(delimiters));
if (first == end(input)) break;
auto last = find_if(first, end(input), is_in(delimiters));
result.emplace_back(first, last);
current = last;
}
return result;
}
to split the other way, by providing a sequence of valid characters:
template<class Container>
std::vector<std::string> split_by_valid_chars(const std::string& input, const Container& valid_chars)
{
std::vector<std::string> result;
for (auto current = begin(input) ; current != end(input) ; )
{
auto first = find_if(current, end(input), is_in(valid_chars));
if (first == end(input)) break;
auto last = find_if(first, end(input), not_in(valid_chars));
result.emplace_back(first, last);
current = last;
}
return result;
}
is_in and not_in are defined thus:
namespace detail {
template<class Container>
struct is_in {
is_in(const Container& charset)
: _charset(charset)
{}
bool operator()(char c) const
{
return find(begin(_charset), end(_charset), c) != end(_charset);
}
const Container& _charset;
};
template<class Container>
struct not_in {
not_in(const Container& charset)
: _charset(charset)
{}
bool operator()(char c) const
{
return find(begin(_charset), end(_charset), c) == end(_charset);
}
const Container& _charset;
};
}
template<class Container>
detail::not_in<Container> not_in(const Container& c)
{
return detail::not_in<Container>(c);
}
template<class Container>
detail::is_in<Container> is_in(const Container& c)
{
return detail::is_in<Container>(c);
}

How to count non-empty vector in a vector recursive type

I have a type that can be define as a vector of vector of vector ... of vector of an integral type. Example:
std::vector<std::vector<std::vector<std::vector< std::vector<signed char> > > > > _data;
I'm searching for an elegant way to determine the number of non-empty vector at the deeper level. I could do that for that example using a 4 encapsulate loop like
for (it0 = data.cbegin() ; it0 != _data.cend() ; ++it0)
for (it1 = *it0.cbegin() ; it1 != *it0.cend() ; ++it1)
for (it2 = *it1.cbegin() ; it2 != *it1.cend() ; ++it2)
for (it3 = *it2.cbegin() ; it3 != *it2.cend() ; ++it3)
nonEmpty += (unsigned int) (*it3.empty());
But how can I create a template (to support vector, list or any kind of container sharing the same API) function to do that for any deep (more than 4 level) ? I think a recursion is the correct way, but don't know how to do that with Template ...
All help & advice will be welcome because I quite sure that there is more than one solution to that.
Here's a C++98 solution using nothing more than basic template specialization:
template<typename T> struct VectorCounter {
/* no count method: this is an error */
};
template<typename U> struct VectorCounter<vector<U> > {
static int count(const vector<U> &v) {
return (int)v.empty();
}
};
template<typename V> struct VectorCounter<vector<vector<V> > > {
static int count(const vector<vector<V> > &v) {
int ret = 0;
for(typename vector<vector<V> >::const_iterator it=v.cbegin(); it!=v.cend(); ++it) {
ret += VectorCounter<vector<V> >::count(*it);
}
return ret;
}
};
template<typename T> int count_nonempty_vectors(const T &v) {
return VectorCounter<T>::count(v);
}
Tested with the following (this test code uses auto as an extension because I'm lazy):
#include <iostream>
#include <vector>
using std::vector;
typedef vector<vector<vector<vector<vector<signed char> > > > > data_t;
int count_fixed(const data_t &data) {
int nonEmpty = 0;
for (auto it0 = data.cbegin() ; it0 != data.cend() ; ++it0)
for (auto it1 = it0->cbegin() ; it1 != it0->cend() ; ++it1)
for (auto it2 = it1->cbegin() ; it2 != it1->cend() ; ++it2)
for (auto it3 = it2->cbegin() ; it3 != it2->cend() ; ++it3)
nonEmpty += (unsigned int)(it3->empty());
return nonEmpty;
}
data_t build_data() {
data_t data(5);
int sz = 0;
for (auto it0 = data.begin() ; it0 != data.end() ; ++it0) {
it0->resize(4);
for (auto it1 = it0->begin() ; it1 != it0->end() ; ++it1) {
it1->resize(3);
for (auto it2 = it1->begin() ; it2 != it1->end() ; ++it2) {
it2->resize(2);
it2->at(0).resize(1);
it2->at(1).resize(0);
}
}
}
return data;
};
int main() {
std::cout << count_fixed(build_data()) << std::endl;
std::cout << count_nonempty_vectors(build_data()) << std::endl;
return 0;
}
Both print out "60".
Maybe Something like that ? (it uses std::enable_if so it's a C++11 answer, but maybe you can use the boost one instead).
template <typename T, typename U>
typename std::enable_if<std::is_same<typename U::value_type,T>::value,std::size_t>::type
non_empties(const U& r)
{
return !r.empty();
}
template <typename T, typename U>
typename std::enable_if<!std::is_same<typename U::value_type,T>::value,std::size_t>::type
non_empties(const U& r)
{
std::size_t res = 0;
for(auto& it : r)
{
res += non_empties<T>(it);
}
return res;
}
usage :
auto non_empty_terminal = non_empties<signed char>(_data);
You have to put the 'stop' type as a template parameter, so maybe it's not ideal.
Live exemple here
You can do pattern matching to count depthness
template <typename Base>
struct NestedVectors //non vector match
{
static const size_t depth=0;
static size_t count_empty(const Base& b)
{
throw std::string("cant be here");
}
};
template <class Rv>
struct NestedVectors<std::vector<Rv> >
{
static const size_t depth=1 + NestedVectors<Rv>::depth ;
static size_t count_empty(const std::vector<Rv>& vec)
{
size_t r=0;
if(NestedVectors<Rv>::depth == 0)
{
if(vec.empty())
return 1;
}
else
{
for(size_t i =0; i < vec.size() ; ++i)
{
r+=NestedVectors<Rv>::count_empty(vec[i]);
}
}
return r;
}
};
int main()
{
typedef std::vector<
std::vector<
std::vector<
std::vector<
std::vector<
signed char
> > > > > data_t;
data_t t;
std::cout << NestedVectors<data_t>::depth << " " << NestedVectors<data_t>::count_empty(t);
}

Cleaning up bidirectional iterator code

I've modified James' flattening iterator to act as a bidirectional iterator if possible, but I don't think my changes are very elegant (particularly relying on a bool to see if the inner iterator has been set). However, I can't seem to come up with a nicer solution. Does anyone have any ideas?
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
#include <iterator>
#include <type_traits>
// An iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename std::iterator_traits<outer_iterator>::value_type::iterator inner_iterator;
typedef typename std::iterator_traits<outer_iterator>::iterator_category outer_category;
typedef typename std::iterator_traits<inner_iterator>::iterator_category inner_category;
typedef typename std::common_type<outer_category, inner_category>::type common_category;
typedef typename std::conditional<std::is_same<common_category, std::random_access_iterator_tag>::value,
std::bidirectional_iterator_tag,
common_category>::type iterator_category;
typedef typename std::iterator_traits<inner_iterator>::value_type value_type;
typedef typename std::iterator_traits<inner_iterator>::difference_type difference_type;
typedef typename std::iterator_traits<inner_iterator>::pointer pointer;
typedef typename std::iterator_traits<inner_iterator>::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it, outer_iterator begin, outer_iterator end)
: outer_it_(it),
outer_begin_(begin),
outer_end_(end),
inner_it_assigned_(false)
{
if (outer_begin_ == outer_end_) { return; }
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
inner_it_assigned_ = true;
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
flattening_iterator& operator--()
{
if(!inner_it_assigned_)
{
if(outer_begin_ != outer_end_)
{
decrement_through_empty_inner_containers();
}
return *this;
}
if(inner_it_ == outer_it_->begin())
{
decrement_through_empty_inner_containers();
}
else
{
--inner_it_;
}
return *this;
}
flattening_iterator operator--(int)
{
flattening_iterator it(*this);
--*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if(a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_assigned_ == false &&
b.inner_it_assigned_ == false)
return true;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
void decrement_through_empty_inner_containers()
{
--outer_it_;
while(outer_it_ != outer_begin_ && outer_it_->begin() == outer_it_->end())
{
--outer_it_;
}
if(outer_it_->begin() != outer_it_->end())
{
inner_it_ = --outer_it_->end();
inner_it_assigned_ = true;
}
}
outer_iterator outer_it_;
outer_iterator outer_begin_;
outer_iterator outer_end_;
inner_iterator inner_it_;
bool inner_it_assigned_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator start, Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(start, first, last);
}
template <typename Iterator>
std::reverse_iterator<flattening_iterator<Iterator>> flatten_reverse(Iterator start, Iterator first, Iterator last)
{
return std::reverse_iterator<flattening_iterator<Iterator>>(flatten(start, first, last));
}
int main()
{
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin(), std::vector<int>());
v.insert(v.begin() + 4, std::vector<int>());
v.push_back(std::vector<int>());
v.push_back(std::vector<int>());
for (auto it(flatten(v.begin(), v.begin(), v.end())), end = flatten(v.end(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
for (auto it(flatten_reverse(v.end(), v.begin(), v.end())), end = flatten_reverse(v.begin(), v.begin(), v.end());
it != end;
++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
std::vector<std::vector<int>> v2;
for (auto it(flatten(v2.end(), v2.begin(), v2.end())), end = flatten(v2.begin(), v2.begin(), v2.end());
it != end;
--it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
}
Great question, and great attempt.
An iterator should always refer to a valid value, or one-past-the-end. *iter should always be valid unless iter == end where end is one-past-the-end. That "one-past-the-end" iterator is the cause of your worries. Either inner_it_ refers to a valid value, or your iterator is one-past-the-end.
James's "one-past-the-end" iterator exists when outer_it_ == outer_end_, and this is the situation you need to check for. This is the only situation in which inner_it_ should have an invalid value. As a result, you can get rid of the bool and just directly check outer_it_ == outer_end_.
Also, I find this line suspect:
inner_it_ = --outer_it_->end();
Is it possible that outer_it_ is a typedef to a pointer? If so, you can't call -- on a pointer value. This will definitely work:
inner_it_ = outer_it_->end();
--inner_it_;
and moreover, it conveys intent better, because the first looks like you're decrementing the end() iterator itself!