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;
}
Related
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;
}
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();
}
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;
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 8 years ago.
Improve this question
I was recently doing a problem on string and suddenly this thing came to my mind that, how could i arrange the words of string in increasing order of their ascii values.
for example there's a string:
ab nn ac mm
so the output should be:
ab ac mm nn
actually i want to arrange them according to sum of ascii values of each letter of a word.
like in above example
ab has a sum of (97+98)=195
ac has a sum of (97+99)=196
and so on...
I want to know is there any efficient method of doing it or is there any function in STL which i can use here?
to make the question more clear Here's a second example if a string is-
acd abz
then output is-
acd abz
as sum of ascii of each letter of "acd" is lesser than that of "abz"
acd sums to (97+99+100)=296
abz sums to (97+98+122)=317
For your custom comparison, this code should do it, using a custom comparison function object (functor), i.e. in this case an object that implements bool operator(x,y) for usage in std::sort:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
struct str_comp
{
bool operator()(const std::string &lhs, const std::string &rhs) const
{
return std::accumulate(std::begin(lhs), std::end(lhs), 0) <
std::accumulate(std::begin(rhs), std::end(rhs), 0);
}
};
int main()
{
std::string input_str {"acd abz aaayyyyy zzzaaaaa"};
std::stringstream ss {input_str};
std::vector<std::string> v_str { std::istream_iterator<std::string>{ss}, {} };
std::sort(std::begin(v_str), std::end(v_str), str_comp());
for (const auto& elem : v_str)
std::cout << elem << std::endl;
}
Or, with a lambda function like a boss:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
int main()
{
std::string input_str {"acd abz aaayyyyy zzzaaaaa"};
std::stringstream ss {input_str};
std::vector<std::string> v_str { std::istream_iterator<std::string>{ss}, {} };
std::sort(std::begin(v_str), std::end(v_str),
[](const std::string& lhs, const std::string& rhs)
{
return std::accumulate(std::begin(lhs), std::end(lhs), 0) <
std::accumulate(std::begin(rhs), std::end(rhs), 0);
}
); // end std::sort
for (const auto& elem : v_str)
std::cout << elem << std::endl;
}
You can split the string and push_back in a vector and sort the vector in the following ways:
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
using namespace std;
int main()
{
vector<string>v;
std::string s="ab nn ac mm";
istringstream t(s);
string ss;
while(t>>ss){
v.push_back(ss);
}
sort(v.begin(),v.end());
for (auto i=v.begin(); i!=v.end(); ++i){
cout<<*i<<endl;
}
return 0;
}
I assume your string has only one space, between a word and a word. Also I assume your string is trimed, which means it doesn't have any space in the head and tail of string. Let me show the code.
std::string sort_by_word_code(const std::string &src)
{
if (src.empty())
return "";
typedef std::string::const_iterator const_iterator;
typedef std::tuple<const_iterator, const_iterator, int> WordTuple;
std::vector<WordTuple> words;
const_iterator i = src.begin(), j;
int code;
while (1)
{
code = 0;
for (j = i; j != src.end() && *j != ' '; ++j)
{
code += *j;
}
words.push_back(WordTuple(i, j, code));
if (j == src.end())
break;
i = j + 1;
}
std::sort(words.begin(), words.end(),
[](const WordTuple &t1, const WordTuple &t2) { return std::get<2>(t1) < std::get<2>(t2); }
);
std::string result;
result.reserve(src.size());
for (auto it = words.begin(); ; )
{
result.insert(result.end(),
std::get<0>(*it),
std::get<1>(*it)
);
++it;
if (it == words.end())
break;
result.push_back(' ');
}
return result;
}
(live example)
The idea is simple. Create a vector which has the sum of ascii and begin/end of each word, sort it, and create a result string from it.
Well this code uses common standard functions/algorithms and is (in my opinion) efficient.
The splitting is done by a stringstream as you can see.
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main()
{
std::string input = "abz acd";
std::vector<std::string> substrings{ std::istream_iterator<std::string>{ std::stringstream{ input } }, std::istream_iterator<std::string>{} };
std::sort(std::begin(substrings), std::end(substrings),
[](const std::string& a, const std::string& b) -> bool
{
auto calcAscii = [](const std::string& str) -> int
{
int ascii = 0;
for (auto const& it : str)
{
ascii += static_cast<int>(it);
}
return ascii;
};
return calcAscii(a) < calcAscii(b);
});
std::string result;
for (auto const& it : substrings)
{
result += it + " ";
}
std::cout << result;
}
If I have the string "2.5 4.2 6.9 1.1", how can I convert that into a vector of doubles as efficiently as possible?
vector<double> convert_string_to_vec(std::string const & str)
{
std::istringstream input{str};
vector<double> output{std::istream_iterator<double>{input},
std::istream_iterator<double>{}};
return output;
}
You'll need to include <sstream> as well as <iterator> for this to work. Here's a working example.
This is how I would generally do it. Possibly not the most efficient way, but very simple.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int main()
{
std::string d("2.5 4.2 6.9 1.1");
std::stringstream s(d);
std::vector<double> result;
double temp;
while(s >> temp)
{
result.push_back(temp);
}
for(size_t i = 0; i < result.size(); ++i)
{
std::cout << result[i] << "\n";
}
return 0;
}
Here's a unique way:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
template <class Facet>
struct erasable_facet : Facet
{
erasable_facet() : Facet(0) { }
~erasable_facet() { }
};
std::vector<double> convert(const std::string& str)
{
using num_get = std::num_get<char>;
erasable_facet<num_get> facet;
std::stringbuf buf(str);
std::vector<double> v;
std::ios ios(nullptr);
std::ios_base::iostate err = std::ios_base::goodbit;
double d;
std::istreambuf_iterator<char> it, end;
do
{
it = facet.get(&buf, end, ios, err, d);
buf.sbumpc(); // skip space
if (!(err & std::ios_base::failbit) &&
!(err & std::ios_base::badbit))
v.push_back(d);
else
return v;
} while (it != end);
return v;
}
int main()
{
std::string str = "1.24 5.32 9.53";
auto v = convert(str);
}