I want to delete element if its value is matching the string "empty", so iterate thorough loop but its not working that way.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main ()
{
std::vector<std::string> myvector;
myvector.push_back("value");
myvector.push_back("value");
myvector.push_back("empty");
myvector.push_back("value");
myvector.push_back("value");
myvector.push_back("empty");
myvector.push_back("empty");
int index = 0;
for(string input: myvector){
if(input == "empty")
myvector.erase(myvector.begin()+index,myvector.begin()+index);
index++;
}
for(string input: myvector){
cout << input << endl;
}
return 0;
}
but we can see that nothing is deleted?
ouput :
value
value
empty
value
value
empty
empty
Looking for something like below but doesn't exists
myvector.erase(myvector.begin(),myvector.end(),"empty");
so how to achieve it in less complexity?
You should use std::remove_if like this:
myvector.erase(std::remove_if(myvector.begin(), myvector.end(), [](const std::string& string){ return (string == "empty"); }), myvector.end());
std::vector<std::string> myvector;
myvector.push_back("value");
myvector.push_back("value");
myvector.push_back("empty");
myvector.push_back("value");
myvector.push_back("value");
myvector.push_back("empty");
myvector.push_back("empty");
auto it = std::remove_if(myvector.begin(), myvector.end(),
[](const std::string& s)
{
return (s== "empty");
});
myvector.erase(it, myvector.end());
use remove_if to put all found "empty" at the end of vector.
use returned iterator to erase them.
Related
I have a string of items (see code). I want to say when a specific item from that list is found. In my example I want the output to be 3 since the item is found after the first two items. I can print out the separate items to the console but I cannot figure out how to do a count on these two items. I think it is because of the while loop... I always get numbers like 11 instead of two separate 1s. Any tips? :)
#include <iostream>
#include <string>
using namespace std;
int main() {
string items = "box,cat,dog,cat";
string delim = ",";
size_t pos = 0;
string token;
string item1 = "dog";
int count = 0;
`;
while ((pos = items.find(delim)) != string::npos)
{
token = items.substr(0, pos);
if (token != item1)
{
cout << token << endl; //here I would like to increment count for every
//item before item1 (dog) is found
items.erase(0, pos + 1);
}
else if (token == item1)
return 0;
}
return 0; //output: box cat
}
I replaced your search algorithm with the method explode, that separates your string by a delimiter and returns a vector, which is better suited for searching and getting the element count:
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>
std::vector<std::string> explode(const std::string& s, char delim)
{
std::vector<std::string> result;
std::istringstream iss(s);
for (std::string token; std::getline(iss, token, delim); )
{
result.push_back(std::move(token));
}
return result;
}
int main()
{
std::string items = "box,cat,dog,cat";
std::string item1 = "dog";
char delim = ',';
auto resultVec = explode(items, delim);
auto itResult = std::find_if(resultVec.begin(), resultVec.end()
, [&item1](const auto& resultString)
{
return item1 == resultString;
});
if (itResult != resultVec.end())
{
auto index(std::distance(resultVec.begin(), itResult) + 1); // index is zero based
std::cout << index;
}
return 0;
}
By using std::find_if you can get the position of item1 by iterator, which you can use with std::distance to get the count of elements that are in front of it.
Credits for the explode method go to this post: Is there an equivalent in C++ of PHP's explode() function?
There are many ways to Rome. Here an additional solution using a std::regex.
But main approach is the same as the accepted answer. Using modern C++17 language elements, it is a little bit more compact.
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
#include <vector>
const std::regex re{ "," };
int main() {
std::string items{ "box,cat,dog,cat" };
// Split String and put all sub-items in a vector
std::vector subItems(std::sregex_token_iterator(items.begin(), items.end(), re, -1), {});
// Search and check if found and show result
if (auto it = std::find(subItems.begin(), subItems.end(), "dog"); it != subItems.end())
std::cout << "Found at position: " << std::distance(subItems.begin(), it) + 1 << '\n';
else
std::cout << "Not found.\n";
return 0;
}
Background:
I got asked this question today in a online practice interview and I had a hard time figuring out a custom comparator to sort. Here is the question
Question:
Implement a document scanning function wordCountEngine, which receives a string document and returns a list of all unique words in it and their number of occurrences, sorted by the number of occurrences in a descending order. If two or more words have the same count, they should be sorted according to their order in the original sentence. Assume that all letters are in english alphabet. You function should be case-insensitive, so for instance, the words “Perfect” and “perfect” should be considered the same word.
The engine should strip out punctuation (even in the middle of a word) and use whitespaces to separate words.
Analyze the time and space complexities of your solution. Try to optimize for time while keeping a polynomial space complexity.
Examples:
input: document = "Practice makes perfect. you'll only
get Perfect by practice. just practice!"
output: [ ["practice", "3"], ["perfect", "2"],
["makes", "1"], ["youll", "1"], ["only", "1"],
["get", "1"], ["by", "1"], ["just", "1"] ]
My idea:
The first think I wanted to do was first get the string without punctuation and all in lower case into a vector of strings. Then I used an unordered_map container to store the string and a count of its occurrence. Where I got stuck was creating a custom comparator to make sure that if I have a string that has the same count then I would sort it based on its precedence in the actual given string.
Code:
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <iterator>
#include <numeric>
#include <algorithm>
using namespace std;
struct cmp
{
bool operator()(std::string& word1, std::string& word2)
{
}
};
vector<vector<string>> wordCountEngine( const string& document )
{
// your code goes here
// Step 1
auto doc = document;
std::string str;
remove_copy_if(doc.begin(), doc.end(), std::back_inserter(str),
std::ptr_fun<int, int>(&std::ispunct));
for(int i = 0; i < str.size(); ++i)
str[i] = tolower(str[i]);
std::stringstream ss(str);
istream_iterator<std::string> begin(ss);
istream_iterator<std::string> end;
std::vector<std::string> vec(begin, end);
// Step 2
std::unordered_map<std::string, int> m;
for(auto word : vec)
m[word]++;
// Step 3
std::vector<std::vector<std::string>> result;
for(auto it : m)
{
result.push_back({it.first, std::to_string(it.second)});
}
return result;
}
int main() {
std::string document = "Practice makes perfect. you'll only get Perfect by practice. just practice!";
auto result = wordCountEngine(document);
for(int i = 0; i < result.size(); ++i)
{
for(int j = 0; j < result[0].size(); ++j)
{
std::cout << result[i][j] << " ";
}
std::cout << "\n";
}
return 0;
}
If anyone can help me with learning how to build a custom comparator for this code I would really appreciate it.
You could use a std::vector<std::pair<std::string, int>>, with each pair representing one word and the number of occurrences of that word in the sequence. Using a vector will help to maintain the order of the original sequence when two or more words have the same count. Finally sort by occurrences.
#include <vector>
#include <algorithm>
#include <string>
#include <sstream>
std::vector<std::vector<std::string>> wordCountEngine(const std::string& document)
{
std::vector<std::pair<std::string, int>> words;
std::istringstream ss(document);
std::string word;
//Loop through words in sequence
while (getline(ss, word, ' '))
{
//Convert to lowercase
std::transform(word.begin(), word.end(), word.begin(), tolower);
//Remove punctuation characters
auto it = std::remove_if(word.begin(), word.end(), [](char c) { return !isalpha(c); });
word.erase(it, word.end());
//Find this word in the result vector
auto pos = std::find_if(words.begin(), words.end(),
[&word](const std::pair<std::string, int>& p) { return p.first == word; });
if (pos == words.end()) {
words.push_back({ word, 1 }); //Doesn't occur -> add it
}
else {
pos->second++; //Increment count
}
}
//Sort vector by word occurrences
std::sort(words.begin(), words.end(),
[](const std::pair<std::string, int>& p1, const std::pair<std::string, int>& p2) { return p1.second > p2.second; });
//Convert to vector<vector<string>>
std::vector<std::vector<std::string>> result;
result.reserve(words.size());
for (auto& p : words)
{
std::vector<std::string> v = { p.first, std::to_string(p.second) };
result.push_back(v);
}
return result;
}
int main()
{
std::string document = "Practice makes perfect. you'll only get Perfect by practice. just practice!";
auto result = wordCountEngine(document);
for (auto& word : result)
{
std::cout << word[0] << ", " << word[1] << std::endl;
}
return 0;
}
Output:
practice, 3
perfect, 2
makes, 1
youll, 1
only, 1
get, 1
by, 1
just, 1
In step2, try this:
std::vector<std::pair<std::pair<std::string, int>, int>> m;
Here, the pair stores the string and this index of its occurance, and the vector stores the pair and the count of its occurances. Write a logic, to sort according to the count first and then if the counts are same, then sort it according to the position of its occurance.
bool sort_vector(const std::pair<const std::pair<std::string,int>,int> &a, const std::pair<const std::pair<std::string,int>,int> &b)
{
if(a.second==b.second)
{
return a.first.second<b.first.second
// This will make sure that if the no of occurances of each string is same, then it will be sorted according to the position of the string
}
return a.second>b.second
//This will make sure that the strings are sorted in the order to return the string having higher no of occurances first.
}
You have to write a logic to count the number of occurrences and the index of occurrence of each word in the string.
I have a vector of strings like
a1 = ["arp", "live", "strong"]
a2 = ["lively", "alive", "harp", "sharp", "armstrong"]
How would I check if armstrong is a substring of strong in a1. using C++
I would do two for loops and check if a string is a substring in a2, but I want an efficient approach.
std::vector<std::string> inArray(std::vector<std::string> &array1, std::vector<std::string> &array2)
{
vector<string> result;
for (string &s : array1)
{
for (string &d : array2)
{
if (s.find(d) != string::npos)
{
cout << d << endl;
}
}
}
return result;
}
int main() {
vector<string> a = { "arp", "live", "strong" };
vector<string> b = { "lively", "alive", "harp", "sharp", "armstrong" };
vector<string> result = inArray(a, b);
}
Given two arrays of strings a1 and a2 return a sorted array r in lexicographical order of the strings of a1 which are substrings of strings of a2.
Example 1:
a1 = ["arp", "live", "strong"]
a2 = ["lively", "alive", "harp", "sharp", "armstrong"]
returns ["arp", "live", "strong"]
First: Use names for the variables and functions that make it easy to identify the purpose
Second: A vector will not magically fill itself.
Third: You are currently searching for the full strings within the substrings (see first)
Fourth: Pass the value by const reference if you don't plan on modifying it.
Fifth: According to your expected answer you don't want duplicates. I would suggest using std::set for this purpose as it doesn't allow duplicates.
#include <vector>
#include <set>
#include <string>
#include <iostream>
using std::set;
using std::vector;
using std::string;
using std::cout;
set<string> getMatchingSubstrings(const vector<string> &subStrings, const vector<string> &fullStrings)
{
set<string> result;
for (const string &fullString : fullStrings)
{
for (const string &subString : subStrings)
{
if (fullString.find(subString) != string::npos)
{
result.insert(subString);
}
}
}
return result;
}
int main() {
vector<string> a = { "arp", "live", "strong" };
vector<string> b = { "lively", "alive", "harp", "sharp", "armstrong" };
set<string> result = getMatchingSubstrings(a, b);
}
A slightly faster approach might be remove the already found substrings from the initial list so you don't have to check for these twice. In this case the result won't be sorted, so if you need to sort again this might not be the best choice.
#include <vector>
#include <string>
#include <iostream>
using std::vector;
using std::string;
using std::cout;
std::vector<std::string> getMatchingSubstrings(const std::vector<std::string> &subStrings, const std::vector<std::string> &fullStrings)
{
vector<string> unmatchedSubStrings = subStrings;
vector<string> matchedSubStrings;
for (const string &fullString : fullStrings)
{
if (unmatchedSubStrings.empty()) break;
for (auto subStringIter = unmatchedSubStrings.begin(); subStringIter != unmatchedSubStrings.end();)
{
const string& subString = *subStringIter;
if (fullString.find(subString) != string::npos)
{
matchedSubStrings.push_back(subString);
subStringIter = unmatchedSubStrings.erase(subStringIter);
}
else
{
++subStringIter;
}
}
}
return matchedSubStrings;
}
int main() {
vector<string> a = { "arp", "live", "strong" };
vector<string> b = { "lively", "alive", "harp", "sharp", "armstrong" };
vector<string> result = getMatchingSubstrings(a, b);
}
std::map< std::string, std::vector<std::string> > families;
// add new families
families["Jones"];
families["Smith"];
families["Doe"];
// add children
families["Jones"].push_back( "Jane" );
families["Jones"].push_back( "Jim" );
I added the values to vector within the map using above method. How can I check if "Jane" exists in map with key "Jones"?
Use the find member function of std::map for this and after that a regular string search:
std::map<std::string, std::vector<std::string> >::const_iterator search = families.find("Jones");
if(search != families.end())
{
std::vector<std::string> s = search->second;
if (std::find(s.begin(), s.end(), "Jane") != s.end())
{
}
}
Here's an example:
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
int main()
{
std::map< std::string, std::vector<std::string> > families;
// add new families
families["Jones"];
families["Smith"];
families["Doe"];
// add children
families["Jones"].push_back( "Jane" );
families["Jones"].push_back( "Jim" );
auto family_iterator = families.find("Jones");
if (family_iterator != families.end())
{
const std::vector<std::string>& family = family_iterator->second;
if (std::find(family.begin(), family.end(), "Jane") != family.end())
std::cout << "found\n";
}
}
Basically, families.find() searches in a map and returns an iterator, for which ->first is the key you've found and ->second is the value: your value is the std::vector<std::string> for the "Jones" family, and for convenience/concision I create a const reference to it.
To search in vector I use std::find() from <algorithm>.
Code available/runnable here
Just check if given surname is in your map and then use std::find. I am not using auto, as from the comments I believe you may not be using C++11-compatible compiler.
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
bool exists(std::map<std::string, std::vector<std::string> >& families,
std::string& name, std::string& surname) {
if(families.find(surname) == families.end()) return 0;
std::vector<std::string> names = families[surname];
return std::find(names.begin(), names.end(), name) != names.end();
}
int main(int argc, char* argv[]) {
std::map<std::string, std::vector<std::string> > families;
// add new families
families["Jones"];
families["Smith"];
families["Doe"];
// add children
families["Jones"].push_back("Jane");
families["Jones"].push_back("Jim");
std::string name("Jane"), surname("Jones");
bool ex1 = exists(families, name, surname);
std::cout << ex1 << std::endl;
return 0;
}
I have a vector, it's contents are like so..
std::vector<string> vec;
vec.push_back("XXXX_LLLL");
vec.push_back("XXXX_HHHH");
vec.push_back("XXXX_XXXX");
I'd like to iterate over the vector and remove the "_" from the string. I've tried using the find-erase idiom as so, with a struct I made to find _.
vec.erase(remove_if(vec.begin(), vec.end(), IsUnderScore2()),vec.end());
But I realized it's not iterating over each individual string in my vector string, so it will never erase the underscore. Is there another method of iterating over a vector, and it's individual components, that can help me here?
Iterate through the vector and use the erase-remove idiom on each string, instead of on the vector elements as you're doing right now
std::vector<string> vec;
vec.push_back("XXXX_LLLL");
vec.push_back("XXXX_HHHH");
vec.push_back("XXXX_XXXX");
for(auto& str : vec) {
str.erase(std::remove(str.begin(), str.end(), '_'),
str.end());
}
C++03 version:
for(std::vector<std::string>::iterator it = vec.begin(), it != vec.end(), ++it) {
it->erase(std::remove(it->begin(), it->end(), '_'),
it->end());
}
Try the following. You can use standard algorithm std::remove applied to each string of the vector.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main()
{
std::vector<std::string> vec;
vec.push_back("XXXX_LLLL");
vec.push_back("XXXX_HHHH");
vec.push_back("XXXX_XXXX");
for ( std::string &s : vec )
{
s.erase( std::remove( s.begin(), s.end(), '_'), s.end() );
}
for ( const std::string &s : vec ) std::cout << s << std::endl;
return 0;
}
The output is
XXXXLLLL
XXXXHHHH
XXXXXXXX
If your compiler does not support the C++ 2011 then you can write
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main()
{
std::vector<std::string> vec;
vec.push_back("XXXX_LLLL");
vec.push_back("XXXX_HHHH");
vec.push_back("XXXX_XXXX");
for (std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); ++it )
{
it->erase( std::remove( it->begin(), it->end(), '_'), it->end() );
}
for (std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); ++it )
{
std::cout << *it << std::endl;
}
return 0;
}
Using regular expressions would look like :
for_each(vec.begin(), vec.end(), [&](string &str) {
regex_replace(str.begin(), str.begin(), str.end(), regex("_"), "");
});
Demo
A range based for loop version might be more readable :
for(auto &str : vec) {
regex_replace(str.begin(), str.begin(), str.end(), regex("_"), "");
}