Elements in a string vector to lower case - c++

I am using one vector of strings and the strings are not in lower case. I want them to covert to lower case.
For converting string to lower case, I am using following method.
std::transform(strCmdLower.begin(), strCmdLower.end(), strCmdLower.begin(), ::tolower);
I can iterate vector and convert each string but I would like to know is there any library functions available for this purpose like above. Copying to new vector also fine.
std::vector<std::string> v1;
v1.push_back("ABC");
v1.push_back("DCE");
std::vector<std::string> v2(v1.begin(), v1.end());

You can still use std::transform with a lambda, e.g.
std::vector<std::string> v1;
v1.push_back("ABC");
v1.push_back("DCE");
std::vector<std::string> v2;
v2.reserve(v1.size());
std::transform(
v1.begin(),
v1.end(),
std::back_inserter(v2),
[](const std::string& in) {
std::string out;
out.reserve(in.size());
std::transform(in.begin(), in.end(), std::back_inserter(out), ::tolower);
return out;
}
);
LIVE

Related

::tolower using std::transform [duplicate]

This question already has answers here:
why does my std::transform retuns nothing/empty string?
(2 answers)
Closed 3 months ago.
Why std::transform doesn't work this way:
std::string tmp = "WELCOME";
std::string out = "";
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
out is empty!
But this works:
std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower);
I don't want the transformation to happen in-place.
You are writing in out-of-bounds memory, since the range of out is smaller than that of tmp. You can store the result in out by applying std::back_inserter.
As user17732522 pointed out, since it's not legal to take the adress of a standard libary function, it's better to pass over a lamda object that calls std::tolower on the character when needed.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), [](auto c) {
return std::tolower(static_cast<unsigned char>(c));
});
One way is to resize the string to allocate sufficient memory for std::transform to populate it.
out.resize(tmp.size()); // <---
std::transform(tmp.begin(), tmp.end(), out.begin(), ::tolower);
Alternatively, as others have mentioned you could use std::back_inserter, which handles inserting for you. We can also call out.reserve to save time on allocations (especially if your string is large).
out.reserve(tmp.size()); // (optional) Smol optimisation.
std::transform(tmp.begin(), tmp.end(), std::back_inserter(out), ::tolower);

Find words that are unique for 1 set out of 3 C++

I have 3 sets containing words.
a: car, boat, table, ball
b: car, goat, helicopter
c: square, car, goat, boat
I need to create a vector or set with words that are contained ONLY in set a.
So the answer would be:
result: table, ball
I tried to make it using set_difference and set_intersection but no luck so far. Can you suggest me something?
I tried
set_difference(a.begin(), a.end(), b.begin(), b.end(), res.begin());
set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());
But the result is empty
Your mistake is here:
set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());
// ^ ^ ^
You iterate over res and write the result in the same set. You need another set to store the result.
A solution would be:
std::set<std::string> a {"car", "boat", "table", "ball"};
std::set<std::string> b {"car", "goat", "helicopter"};
std::set<std::string> c {"square", "car", "goat", "boat"};
std::set<std::string> tmp;
std::set<std::string> res;
// Difference between a and b --> stored in tmp
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));
// Difference between tmp and c --> stored in res
std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));
for(const std::string & s : res)
std::cout << s << '\n';
Output:
ball
table
Live example
Note: If we look at the documentation of std::set_difference, we can see:
Copies the elements from the sorted range [first1, last1) which are not found in the sorted range [first2, last2) to the range beginning at d_first.
The resulting range is also sorted. Equivalent elements are treated individually, that is, if some element is found m times in [first1, last1) and n times in [first2, last2), it will be copied to d_first exactly std::max(m-n, 0) times. The resulting range cannot overlap with either of the input ranges.
emphasis mine
So if you want to use another container that does not guarantee the uniqueness of its elements (for example std::vector), you need to ensure that each element does not appear several times in your container by yourself.
Note 2: If you don't want to bother with the tmp set (which is useless after getting the res set), you can put it inside a bloc so that it will be destroyed afterwards:
std::set<std::string> res;
{
std::set<std::string> tmp;
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));
std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));
} // tmp destroyed here
Live example
Without sharing your code, we can only guess as to what your code is doing wrong.
Here's what I did. I wrapped the difference logic in a helper operator-. I used std::unordered_set intentionally, because they can't be used directly in std::set_difference.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_set>
#include <vector>
using std::cout;
using std::inserter;
using std::ostream;
using std::set_difference;
using std::sort;
using std::string;
using std::unordered_set;
using std::vector;
namespace {
unordered_set<string> operator-(unordered_set<string> const& minuend, unordered_set<string> const& subtrahend) {
vector<string> m(minuend.begin(), minuend.end());
vector<string> s(subtrahend.begin(), subtrahend.end());
sort(m.begin(), m.end());
sort(s.begin(), s.end());
unordered_set<string> diff;
set_difference(m.begin(), m.end(), s.begin(), s.end(), inserter(diff, diff.begin()));
return diff;
}
ostream& operator<<(ostream& out, unordered_set<string> const& container) {
char const* sep = " ";
out << "{";
for (auto const& s : container) {
out << sep << "\"" << s << "\"";
sep = ", ";
}
out << " }";
return out;
}
}
int main() {
auto a = unordered_set<string>{ "car", "boat", "table", "ball" };
auto b = unordered_set<string>{ "car", "goat", "helicopter" };
auto c = unordered_set<string>{ "square", "car", "goat", "boat" };
auto d = a - b - c;
cout << d << "\n";
}
UPDATE answering Fareanor's questions
Why do you use std::unordered_set (instead of std::set)?
I chose the unordered_set to demonstrate that the set_difference required a sorted container. The unordered_set lacks that feature.
And the original poster, in the original unedited question, did not provide the details of what kind of container was being used.
Why do you convert it into a std::vector that will need to be sorted (instead of converting into std::set)?
A vector is a very efficient container, since the elements in it have a locality, and therefore good cache. It is my go-to container.
A set has a lot more memory allocations because it is a mesh of nodes, and lacks the locality.
The string object being contained may lack the locality anyway, since it is basically a smart pointer to a character array. But because of small string optimization (SSO) and these are small strings, it won't also be allocating off the heap.
In the original poster's scenario, there are only a few items in each of the container, so the efficiency concerns are negligible. But worth thinking about, if the problem domain was scaled up.
I think you should have used std::set (without any clarification from the OP at least) and if the user got a std::unordered_set instead, it is up to him to convert it to a proper std::set and then call your operator-().
That is a viable option. Lacking the context at the time, I considered this a "worse case scenario" because the unordered_set container does not fulfill the requirement of the set_difference algorithm.

How to build std::vector<std::string> from std::initializer_list<char const*>

What is the correct way to build an std::vector<std::string> from an std::initializer_list<char const*>? I can iterate through the initializer list and manually build up the vector as such:
std::initializer_list<char const*> values;
std::vector<std::string> vec;
for(auto v : values) {
vec.push_back(v);
}
Seems quite verbose. Suggestions?
std::vector has a constructor that accepts an iterator range as input, as long as the dereferenced values are convertible to the vector's value_type:
std::vector<std::string> vec(values.begin(), values.end());

wordDiff must return array of strings with word in s that are not in t, in the order they appear in s

using namespace std;
vector<string> wordDiff(string s, string t)
{
istringstream parse_s(s);
vector<string> words_s(istream_iterator<string>(parse_s), {});
istringstream parse_t(t);
vector<string> words_t(istream_iterator<string>(parse_t), {});
sort(words_s.begin(), words_s.end());
sort(words_t.begin(), words_t.end());
vector<string> funk;
set_difference(words_s.begin(), words_s.end(),
words_t.begin(), words_t.end(),
back_inserter(ret));
return funk;
}
so far i am able to get the array of strings with words in s that are not i t with set_difference however i am unable to get the array in the order of s
The simplest solution is to put all excluded words into a std::unordered_set instead of a std::vector.
Then you can use the set to check each word in your word list if it has to be excluded or not. This way, you no longer need to sort. All you need is a copy_if and a lambda.
Simple solution is not to sort words_s (only words_t) and use std::remove_if:
sort(words_t.begin(), words_t.end());
auto it = std::remove_if( words_s.begin(), words_s.end(), [words_t]( const std::string &str ) {
return std::find( words_t.begin(), words_t.end(), str ) != words_t.end() );
} );
words_s.erase( it, words_s.end() );
you may want consider to use std::unordered_set instead of sorted vector for words_t

I have a function returning a vector of strings. It takes two strings and is to return the strings in one that are missing in the other

vector<string> missingWords(string s, string t) {
//new array to hold s and t
vector<string> arr;
arr.push_back(s);
arr.push_back(t);
//loop through vector
for(auto i=0; i <= arr.size(); i++)
//erase t
arr.erase(remove(arr.begin(), arr.end(), t), arr.end());
return arr;
}
This function just deletes t, but I want to delete t completely so that if there are strings in t that are in s the strings in s will also be deleted.
Based on the name missingWords, I'm going to guess that you want to take each input string, and tokenize it into words. Then you want to create an output that contains all the words present in one that aren't present in the other.
If that's correct, we might consider something like this:
std::vector<std::string> missingWords(std::string const &a, std::string const &b)
{
std::istringstream parse_a(a);
std::vector<std::string> words_a(std::istream_iterator<std::string>(parse_a), {});
std::istringstream parse_b(b);
std::vector<std::string> words_b(std::istream_iterator<std::string>(parse_b), {});
std::sort(words_a.begin(), words_a.end());
std::sort(words_b.begin(), words_b.end());
std::vector<std::string> ret;
std::set_difference(words_a.begin(), words_a.end(),
words_b.begin(), words_b.end(),
std::back_inserter(ret));
return ret;
}
So, we start by breaking each string up into words. Then we sort the words in each vector. Then we find the difference between the two.
Note that as it stands right now, if one string contains two copies of a word, and the other contains only one copy of that word, this will show that word in the list of differences.
One easy way to eliminate that would be to use an std::set instead of an std::vector (and this lets us eliminate the sort as well):
std::vector<std::string> missingWords(std::string const &a, std::string const &b)
{
std::istringstream parse_a(a);
std::set<std::string> words_a(std::istream_iterator<std::string>(parse_a), {});
std::istringstream parse_b(b);
std::set<std::string> words_b(std::istream_iterator<std::string>(parse_b), {});
std::vector<std::string> ret;
std::set_difference(words_a.begin(), words_a.end(),
words_b.begin(), words_b.end(),
std::back_inserter(ret));
return ret;
}