I have an std::vector of std::strings containing data similar to this:
[0] = ""
[1] = "Abc"
[2] = "Def"
[3] = ""
[4] = "Ghi"
[5] = ""
[6] = ""
How can I get a vector containing the 4 strings from 1 to 4? (i.e. I want to trim all blank strings from the start and end of the vector):
[0] = "Abc"
[1] = "Def"
[2] = ""
[3] = "Ghi"
Currently, I am using a forward iterator to make my way up to "Abc" and a reverse iterator to make my way back to "Ghi", and then constructing a new vector using those iterators. This method works, but I want to know if there is an easier way to trim these elements.
P.S. I'm a C++ noob.
Edit
Also, I should mention that the vector may be composed entirely of blank strings, in which case a 0-sized vector would be the desired result.
How about this, with a predicate:
class StringNotEmpty
{
bool operator()(const std::string& s) { return !s.empty(); }
};
Now to trim:
vec.erase(std::find_if(vec.rbegin(), vec.rend(), StringNotEmpty()).base(), vec.end());
vec.erase(vec.begin(), std::find_if(vec.begin(), vec.end(), StringNotEmpty()));
There might be an off-by-one on the .base() call, but the general idea should work.
Your approach is reasonable. Another approach is to find the first string, and then copy all successive strings to the beginning (and following) elements in the vector, after which you trim the end of the vector.
Unless this is a critical piece of code that is slowing down your application, it matters more that it works than that you have the most efficient implementation possible.
What you've done is good. But if you want to shorten a single container "in place" rather than make a copy of a subrange, you can erase the other ranges from the vector.
// Probably similar to what you already have: Find iterators for the range to keep.
std::vector<std::string>::iterator start=strs.begin(), stop=strs.end();
start = strs.begin();
while (start != stop && start->empty()) ++start;
if (start == stop) {
// all strings were empty!
strs.clear();
} else {
while (start != --stop && stop->empty()) ;
++stop;
// std::vector<std::string>(start, stop) is the desired subrange
strs.erase(stop, strs.end());
strs.erase(strs.begin(), start);
}
But I agree with #Nathan: If you what you already have makes more sense to you than this, keep it, unless you know or strongly suspect that there will be an awful lot of strings involved.
typedef std::vector<std::strings> Strings;
Strings myStrings;
//... currently myStrings contains "","Abc","Def","","Ghi","",""
Strings::iterator itNewBegin = myStrings.begin();
Strings::iterator itNewEnd = myStrings.end();
std::advance(itNewBegin,1);
std::advance(itNewEnd,4);
String myTrimmedStrings(itNewBegin,itNewEnd);
//... currently myTrimmedStringscontains "Abc","Def","","Ghi"
By curiosity, I would like to see your code with the use of the reverse iterator.
I don't grasp the way you contruct new vector from two iterators that have different directions.
Iterators are definitely the way to go. They make the code much more readable and intuitive.
#include <algorithm>
typedef std::vector<std::string> strVec_t;
bool notEmpty(std::string s){
return !s.empty();
}
void trim(strVec_t& vec){
//get the first and last non-empty elements
strVec_t::const_iterator first = std::find_if(vec.begin(), vec.end(), notEmpty);
strVec_t::const_reverse_iterator last = std::find_if(vec.rbegin(), vec.rend(), notEmpty);
//if every element is an empty string, delete them all
if (first == vec.end()){
vec.clear();
//make a new vector from the first to the last non-empty elements
} else {
vec = strVec_t(first, last.base());
}
}
Related
I have a legacy code simplified as below:
std::map< int, std::vector<int> > list2D;
given a range( a number range for example between 10 - 90 ) I need need to filter the map first and eliminate the elements which are not between the number range. Again given the elements
20 -> {10,20}, 40 -> {500,200}, 100 -> {1,2,3} given the range is 10-90
I need the filter the one with 100.
Than I need to concatenate all the vectors left. So result will be {10,20,500,200} .
My legacy code was doing that with two for loops. I am planning to use lower_bound function for filtering step. But it seems I will still need a for loop. Simplified version can be seen below. To be honest the version with 2 for loops was looking more simple.
void simplifiedFoo ()
{
std::vector<int> returnVal;
std::map<int, std::vector<int> > list2D = {{20 , {10,20} },
{40 , {500,200}},
{100 , {1, 2, 3}} };
auto itlow=list2D.lower_bound (10);
auto itup=list2D.lower_bound (50);
if ( itlow != list2D.end() && itup == list2D.end() )// Don't like this if even not sure if it is correct.
--itup;
while ( itlow != itup) // How to avoid second loop
{
returnVal.insert(returnVal.end(), itlow->second.begin(), itlow->second.end());
++itlow;
}
for ( auto elem : returnVal)
std::cout << elem << " " ;
return 0;
}
What can be the preferable clean way for that(I need to implement this with vs2010)? Is there clean way I can achieve "reduce" functionality in C++ for my case?
Not sure if this is what you mean, but if you are looking for using "std oneliners", this would work:
std::for_each(itlow, itup,
[&returnVal](const auto& elem) {
returnVal.insert(returnVal.end(), elem.second.begin(), elem.second.end());
});
Now, can we call this "the preferable clean way for that"? I think it is debatable.
I think, you must use upper_bound for the second value, otherwise the upper value will be excluded
auto first = list2D.lower_bound(10);
auto last = list2D.upper_bound(90);
You don't need to check, if the lower iterator is != end() and the upper one is == end(). So your loop just becomes
for (; first != last; ++first) {
// ...
}
If the upper iterator were equal to end(), it would be equivalent to
for (; first != list2D.end(); ++first) {
// ...
}
In C++, I start with a vector matchingFirstAndLast, which is filled with words that match some criteria (based on userInput). On average, its very large, so I apply filters on it and create a vector to store what was filtered. I feel like that is not elegant; I would like to filter (like for F# lists) instead of making a new vector.
In other words, I want to have one vector that gets filtered over and over without creating new ones.
My code's data flow :: matchingFirstAndLast |> sequenced |> appropriateLength |> finalSuggestions`
// My code ( in case needed )
vector<finalwords> compute (string userInput,
vector<string>dictionary,
vector<string>popular,
vector<string>keyboard_layout)
{
//matchingFirstAndLast will hold words with the same first and last letter as the input string
vector<string>matchingFirstAndLast;
int inputLength = userInput.length();
//for every word in the big dictionary vector, look for first and last letter similarites
for (string &x : dictionary)
if (userInput[0] == x.front() && userInput[inputLength - 1] == x.back())
matchingFirstAndLast.push_back (x);
//sequenced will hold words whose letters are found in sequence in the userInput string
vector<string>sequenced;
for (string &x : matchingFirstAndLast)
if (FoundInSequence (userInput, x))
sequenced.push_back (x);
//determine the minimum word length based on the number of times the string changes
//rows on the keyboard.
int minLength = GetMinWordLength (userInput, keyboard_layout);
//appropriateLength will hold all words longer than the minLength
vector<string>appropriateLength;
for (auto &x : sequenced)
if (x.length() > minLength || minLength < 0)
appropriateLength.push_back (x);
vector<finalwords> finalSuggestions;
for (string &x : appropriateLength)
if (find (popular.begin(), popular.end(), x) != popular.end()) //word found in popular.txt
finalSuggestions.push_back (finalwords (x, true, true, edit_distance (userInput, x)));
else
finalSuggestions.push_back (finalwords (x, false, true, edit_distance (userInput, x)));
//sort the returned vector by most popular first
sortResults (finalSuggestions);
return finalSuggestions;
}//end compute(...)
In python, for example, this is possible
suggestions = filter(lambda x: x[0] == path[0] and x[-1] == path[-1], WORDS)
suggestions = filter(lambda x: match(path, x), suggestions)
suggestions = filter(lambda x: len(x) > min_length, suggestions)
This never stores the 'filtered' data into a new container.
Like the python example ^, I would like some way of doing this in C++
'Filter' is a bit ambiguous. From my perspective when you say you're trying to 'filter' a vector it implies, to me, that you want to create another vector with only some of the elements from the original list. But the text of your post makes it clear that that's not what you want. So my conclusion is what you're really after is selective iteration of the elements in the vector. In other words, you want to iterate over the elements in the list, but only act on some of them.
If that's the case, then I'd suggest using a fictional Std Lib algorithm, for_each_if. I say fictional because there is no such algorithm, but I've implemented it in the past and it's not hard.
Something along these lines should about do it (untested):
template <typename InIt, typename Predicate, typename UnaryFunction>
UnaryFunction for_each_if (InIt first, InIt last, UnaryFunction fn, Predicate pr)
{
for (; first != last; ++first)
{
if (pr (*first))
fn (*first);
}
return fn;
}
Using this is similar to using std::for_each, except you also specify a predicate like you would with copy_if. Assuming C++11, you could do all of this with lambdas.
Think of implementing your own type of iterator that will return strings that correspond to some set of predicates.
Example pseudocode:
struct iter {
std::vector<Predicate> predicates;
std::vector<string>& values;
int currentValue = 0;
string nextValue() {
return values[currentValue++];
}
bool hasValue() {
while (currentValue < values.count() {
bool found = true;
for (auto& pred : predicates)
if (!pred(values[currentValue])) {
++currentValue; found = false; break;
}
if (found) return true;
}
return false;
}
};
On the other hand you may search for some implementations of LINQ for C++.
I've implmented one by myself:
github It is not for production use, but you may find some ideas interesting.
I wouldn't be surprised if this is a duplicate but I've searched all around and have only found posts on finding specific elements in vectors.
I have a code that splits a string into a vector of all the individual words in the string (split by spaces) and later assigns each index of that vector to individual strings (so just taking a sentence and splitting it up into words) but I found if I try to index a certain element of the vector that doesn't exist I get all sorts of errors. For example, if my vector has 5 elements and later I say:
string x = names[6];
then, since there is no names[6], the code breaks. I want to add an "if" statement that essentially says "if names[6] exists, string x = names [6]" but I don't know how to check if names[6] (or any other index of the vector) exists.
I have tried:
if (std::find(names.begin(), names.end(), names[4]) != names.end())
{
string x = names[4];
}
else
{
}
but I end up with the same errors if names[4] doesn't exist.
If someone could please let me know how to do this or reference me to another post that explains this that would be great,
-thanks
An index in an std::vector exists iff index < vector.size(). You may test for that:
if (names.size() > 4)
string x = names[4];
What about putting all the entries of the vector into strings by iterating them?
for (const auto& e : names) {
// You can use e here
}
If you really want to check if an index exists you could check with at:
try {
std::string x = names.at(position);
}
catch (const std::out_of_range& oor) {
//Nothing at this spot!
}
You could also check with vector.size().
I am using C++ and i have 2 vectors that a related to each other:
vector<double> val = {.3,.5,.2,.4};
vector<string> str = {'a','b','c','d'};
I would like to search val for the max, and then return the string from str in the same position:
vector<double>::const_iterator it;
it = max_element(val.begin(), val.end());
So, how can i use it inside str to get the letter?
string lettter;
letter = str.at(it-> ????? );
Thank!!!
How about
letter = str.at(it - val.begin());
?
(Rationale)
You can find out how far it is from the beginning of val and then use that to index str:
str[std::distance(std::begin(val), it)]
By using std::distance, this will still work if you change the type of val to a container whose iterator does not provide random access. However, when using it on a random access iterator, you will still get constant time complexity. Using std::begin allows you to change val to an C-style array if you ever wanted to.
It's worth mentioning that you should be initialising str with:
vector<string> str = {"a","b","c","d"};
std::string has no constructor that takes a char.
Get the index of the element like so:
auto index = std::distance(val.begin(), it);
Then index into your string array:
auto letter = str[index];
Note that if these values are correlated, you should probably forcibly keep them together:
typedef std::pair<double, string> valstr_pair;
std::vector<valstr_pair> valstr;
auto it = std::max_element(valstr.begin(), valstr.end(),
[](const valstr_pair& first, const valstr_pair& second)
{
return first.first < second.first; // compare values
});
it->first; // max value
it->second; // string of max value
This will give you the distance between the iterator returned by max_element() and the beginning of the vector:
std::distance(val.begin(), it).
You can then use it as an index into str.
I am new to C++, and am continuously told to use std::vector instead of new[].
I am trying to achieve this functionality, in where I know the size of the vector and want to assign to it randomly (not sequentially).
However, when running this, my program terminates with no error output, so I am stumped.
vector<string> v1;
v1.resize(2);
v1.insert(v1.begin() + 1, "world");
v1.insert(v1.begin() + 0, "world");
cout << v1.at(1) << endl;
Don't give up, it's easier than that
vector<string> v1(2);
v1[1] = "world";
v1[0] = "world";
cout << v1[1] << endl;
vector::insert is for when you want to add items to your vector, Not when you want to replace ones that are already there, vector::insert changes the size of the vector in other words.
First you resize it to have two empty strings:
{"", ""}
Then you insert "world" before begin() + 1 or the 2nd element:
{"", "world", ""}
Then you insert "world" before begin() or the 1st element:
{"world", "", "world, ""}
Then you access the 2nd element with v1.at(1) and get the empty string.
Presumably, you don't want std::vector::insert which inserts new elements between existing elements. You want to do this as you would with arrays, with operator[]:
vector<string> v1(2);
v1[1] = "world";
v1[0] = "world";
cout << v1.at(1) << endl;
To randomly assign
Simply use the index (obviously, validate that it's < size)
v1[index] = value;
To randomly insert (validate that index < size)
v1.insert(v1.begin() + index, value);
To sequentially insert at the end / append (no need for an index, your value will be inserted at the end of the vector)
v1.push_back(value);
If you plan on inserting many values, consider calling reserve() on your vector so that enough memory can be allocated to store all of your items, otherwise as you insert your data you may end up with many reallocation as the vector grows
Your Program is working correctly. Your error is in the logic of your code.
Insert doesn't change the string stored at index 1. It places a string at position 1, and moves all indexes after 1 to the right.
start first insert second insert
("","") -> ("", "world, "") -> ("world","","world","")
So when you print v1.at(1) you are printing an empty string.
To fix the issue, you would want to use:
v1.at(1)="world"
v1.at(0)="world"
--or--
v1[1] ="world"
v1[0] ="world"
The two solutions are equivalent, however the second will not do any bounds checking. The first will throw an error if there is an out of bounds error. This shouldn't matter as long as you can gaurantee that you will never index out of bounds.
Like many have stated, you can can just use operator[] to reassign old values, given that you've already sized the vector or filled it with other values.
If your array will always have a fixed size you can use std::array, which should provide a performance boost, at the sacrifice of the ability to resize the array or determine its size at runtime.
std::array<std::string,2> a1;
a1[0] = "world";
a1[1] = "world2";
std::cout<<a1.at(1)<<std::endl; //outputs world2
Note that the size must be static, so you can't do something like this:
int numStrings;
std::cin>>numStrings;
std::array<std::string,numStrings> a2; //ERROR
Unfortunately, I don't think there is any way of initializing an std::array without a default constructor, other than using an initialization list.
struct T
{
T(std::string s):str(s){} //no default constructor
std::string str;
}
std::array<T,2> a3 = {T(""), ""}; //you can use a conversion constructor implicitly
Obviously, this isn't practical if you want an array with a sizable number of objects.