Does c++11 offer similar solution as implemented in python maketrans/translate? - c++

Does c++11 offer elegant solution as implemented in python maketrans/translate?
from string import maketrans
intab = "aeiou"
outtab = "12345"
trantab = maketrans(intab, outtab)
str = "this is string example....wow!!!";
print str.translate(trantab);

As as I'm aware there's no built-in function for this, but you could conceivably implement one:
#include <functional>
#include <string>
#include <unordered_map>
std::function<std::string(std::string)>
maketrans(const std::string& from, const std::string& to) {
std::unordered_map<char, char> map;
for (std::string::size_type i = 0;
i != std::min(from.size(), to.size()); ++i) {
map[from[i]] = to[i];
}
return [=](std::string s) {
for (auto& c : s) {
const auto mapped_c = map.find(c);
if (mapped_c != map.end()) {
c = mapped_c->second;
}
}
return s;
};
}
#include <iostream>
int main() {
const std::string intab = "aeiou";
const std::string outtab = "12345";
const auto translate = maketrans(intab, outtab);
const std::string str = "this is string example....wow!!!";
std::cout << translate(str) << std::endl;
return 0;
}

My try. Would would need to make a helper to create the map from two strings:
#include <iostream>
#include <string>
#include <map>
using namespace std;
using TransMap = std::map<char, char>;
void translate(std::string& string, TransMap map ){
for(auto& c : string){
const auto itr = map.find(c);
if(itr != map.end()) c = itr->second;
}
}
int main() {
std::string test = "123456789";
TransMap map{ {'1', 'a'}, {'2', 'b'}, {'3', 'c'}, {'4', 'd'}, {'5', 'e'} };
translate(test, map);
std::cout << test << '\n';
return 0;
}

Related

Converting a vector<char *> to char ** array? [duplicate]

This question already has answers here:
convert vector<string> into char** C++
(4 answers)
Closed 2 years ago.
I'm trying to convert a vector<char * > into a char** array. When I print parsed[0], I get garbage. How would I fix this?
char **parse(vector<string> arr){
vector<char*> res;
for(int i = 1; i < arr.size(); i++){
string s = arr.at(i);
char temp[s.length() + 1];
s.copy(temp, s.length()+1);
temp[s.length()] = '\0';
res.push_back(temp);
}
res.push_back(NULL);
char **parsed = res.data();
cout << parsed[0] << endl;
return parsed;
}
Just going by the title calling vector<char*>.data() seems to be the most straightforward way:
#include <type_traits>
#include <vector>
int main()
{
std::vector<char*> chars{};
static_assert(std::is_same_v<char**, decltype(chars.data())>);
}
This seems like a really convoluted method of just doing:
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
std::vector<const char*> to_ptrs(const std::vector<std::string>& arr) {
std::vector<const char*> res;
for (auto& s : arr) {
res.emplace_back(s.c_str());
}
return res;
}
int main() {
std::vector<std::string> input = {
"test",
"example",
"of",
"strings"
};
auto ptr_vec = to_ptrs(input); // To avoid dangling temporary
const char** result = ptr_vec.data();
for (size_t i = 0; i < input.size(); ++i) {
std::cout << result[i] << std::endl;
}
return 0;
}
Or if you prefer a C++20 solution with std::transform:
std::vector<const char*> to_ptrs(const std::vector<std::string>& arr) {
std::vector<const char*> res;
std::transform(arr.begin(), arr.end(), std::back_inserter(res), [](const std::string& s) -> const char* { return s.c_str(); });
return res;
}
Note conversion to char** is not possible because the result of c_str() is const char*.
Note: Any alterations to the original strings or string data invalidates these pointers.

Modify strings within a vector? (Without using loops)

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;
}

check if a vector contains a substring of another vector

I have file names and I need to check if these files end with any extension of the vector extensions; I would like to use some of the algorithms that are in the library instead of how I have done it, is there any way?
#include <iostream>
#include <algorithm>
#include <vector>
std::string tail(const std::string &st, const size_t len)
{
if (len >= st.size())
return st;
return st.substr(st.size() - len);
}
std::vector<std::string> filtered_files(const std::vector<std::string>& files, const std::vector<std::string>& extensions) {
std::vector<std::string> re;
for(const std::string f : files) {
for(const std::string ex : extensions) {
if(ex == tail(f,ex.size())) {
re.push_back(std::move(f));
break;
}
}
}
return re;
}
int main(int argc, char **argv) {
std::vector<std::string> v{"main.cpp","main.c","main.py"};
std::vector<std::string> re = filtered_files(v,{".c",".cpp"});
for(const std::string s :re) {
std::cout << s << '\n';
}
}
Have a look at the std::find_if() standard algorithm in place of the inner loop. You can use the std::string::compare() method to perform substring comparisons without having to actually allocate new std::string objects, as your loops and tail() function currently do. The only string allocations you need are for the strings pushed into re (and even that allocation can be avoided if you return a std::vector<std::string*> of pointers that point to the strings in te files vector).
Try this:
#include <iostream>
#include <algorithm>
#include <vector>
std::vector<std::string> filtered_files(const std::vector<std::string>& files, const std::vector<std::string>& extensions)
{
std::vector<std::string> re;
for(const std::string &f : files)
{
if (std::find_if(extensions.begin(), extensions.end(),
[&](const std::string &ex){
return (f.size() >= ex.size()) && (f.compare(f.size()-ex.size(), ex.size(), ex) == 0);
}
) != extensions.end())
{
re.push_back(f);
}
}
return re;
}
Live Demo
There are actually many ways of solving this, personally, this is the way I've achieved it before
#include <iostream>
#include <algorithm>
#include <string>
#include <functional>
#include <vector>
int main()
{
std::vector<std::string> v{"main.cpp","main.c","main.py"};
std::vector<std::string> ext{".cpp", ".c"};
std::vector<std::string> res;
for (auto& s : v) {
auto pos = s.find_last_of('.');
if (pos != s.npos) {
char* str = &s[pos];
if (std::any_of(ext.begin(), ext.end(),
[str](const string& a) { return str == a; })) {
res.push_back(s);
}
}
}
for (auto& r : res)
cout << r << endl;
return 0;
}

How to use count() function in a vector<struct> according to specific criteria

I have a vector of type struct with some elements, and trying to count the number of occurrences of an element(value) in its corresponding column of the vector. I know how to count on a simple vector, e.g on vector of type string. But am stuck on vector<struct>. Any possible solution or suggestion?
Sample code:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
struct my_struct
{
std::string first_name;
std::string last_name;
};
int main()
{
std::vector<my_struct> my_vector(5);
my_vector[0].first_name = "David";
my_vector[0].last_name = "Andriw";
my_vector[1].first_name = "Jhon";
my_vector[1].last_name = "Monta";
my_vector[2].first_name = "Jams";
my_vector[2].last_name = "Ruth";
my_vector[3].first_name = "David";
my_vector[3].last_name = "AAA";
my_vector[4].first_name = "Jhon";
my_vector[4].last_name = "BBB";
for(int i = 0; i < my_vector.size(); i++)
{
int my_count=count(my_vector.begin(), my_vector.end(),my_vector[i].first_name);
/*I need help to count the number of occerencess of each "First_name" in a vector
For example: First_Name:- David COUNT:- 2 ...and so on for each first_names*/
std::cout << "First_Name: " << my_vector[i].first_name << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
but, the same code for a vector of type string,std::vector<std::string> works properly. see below:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> my_vector;
my_vector.push_back("David");
my_vector.push_back("Jhon");
my_vector.push_back("Jams");
my_vector.push_back("David");
my_vector.push_back("Jhon");
for(int i = 0; i < my_vector.size(); i++)
{
int my_count = count(my_vector.begin(), my_vector.end(),my_vector[i]); //this works good
std::cout << "First_Name: " << my_vector[i] << "\tCOUNT: " << my_count << std::endl;
}
return 0;
}
You have to use std::count_if with correct predicate:
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
[&](const my_struct& s) {
return s.first_name == my_vector[i].first_name;
});
Demo
The functor to replace lambda in C++03:
struct CompareFirstName
{
explicit CompareFirstName(const std::string& s) : first_name(s) {}
bool operator () (const my_struct& person) const
{
return person.first_name == first_name;
}
std::string first_name;
};
and then
int my_count = std::count_if(my_vector.begin(), my_vector.end(),
CompareFirstName(my_vector[i].first_name));
Demo

Sort a list of file names by number at end

I have a list of files :
foo10.tif
foo2.tif
...
foo102.tif
I would like to sort them according to number at end of file name.
Preferably using c++11 and lambdas.
Here is something that works. Let me know if you have any issues with it:
#include <vector>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
vector<string> aFiles =
{
"foo10.tif",
"foo2.tif",
"foo102.tif",
};
sort(begin(aFiles), end(aFiles), [](const string& a, const string& b)
{
auto RemoveExtension = [](const string& s) -> string
{
string::size_type pos = s.find_last_of('.');
if (pos != string::npos)
return s.substr(0, pos);
return s;
};
string partA = RemoveExtension(a), partB = RemoveExtension(b);
if (partA.length() != partB.length())
return partA.length() < partB.length();
return partA < partB;
} );
for (string s : aFiles)
cout << s << endl;
}