I am supposed to read some data (specifically string datatype) and store each element in a vector. Now I have to check if any of the different strings that were inputted match in size, and if they do I have to see if there are any matching letters. Now my question is how do I compare what's inside the vector (first the size and then the different letters). Is it possible?
Say I have
HELLO
and
HELLA
They have the same size, and 4 letters that match.
This is what I am trying to accomplish.
The code that I have does not work given my ignorance about the matter.
Thank you in advance.
vector <string> myVector;
//insert data insdide of the vector
myVector.push_back("Hello");
myVector.push_back("Hello");
myVector.push_back("Hello2");
myVector.push_back("Hello3");
myVector.push_back("Hello4");
//This is wrong
for (unsigned int i = 0; i < myVector.size(); i++) {
if (myVector[i].size == myVector[i+1].size()){
cout << "SAME SIZE" << endl;
}
}
return 0;
You just have make a simple mistake for size() function and you are trying to access the element which is not present by using i+1 for last iteration.
So just change your for loop just as below
for (unsigned int i = 1; i < myVector.size(); i++)
{
if (myVector[i].size() == myVector[i-1].size()) // .size() should be used
{
cout << "SAME SIZE" << endl;
}
}
Here's a way of writing it:
// returns true if #param s1 and #param s2 are equal in letters
bool isEqual(const string& s1, const string& s2) {
if(s1.size() != s2.size())
return false;
bool equal = false;
// iterates over all the characters in s1 and s2 and compare them
for(auto ch1 = s1.cbegin(), ch2 = s2.cbegin(); ch1 != s1.cend(),ch2!= s2.cend(); ch1++, ch2++) {
if(*ch1 == *ch2)
equal = true;
else
return false;
}
return equal;
}
// type of iter is vector<string>::const_iterator meaning it can only read the value
for (auto iter = myVector.cbegin(); iter != myVector.cend() - 1; iter++){
if(isEqual(*iter, *(iter + 1)))
std::cout << *iter << " equal " << *(iter + 1) << endl;
else
std::cout << *iter << " different " << *(iter + 1) << endl;
}
Here, I used iterators(you should write code in modern C++, avoid using subscript).
Related
I have the following simple code. I declare a vector and initialize it with one value 21 in this case. And then i am trying to find that value in the vector using find. I can see that the element "21" in this case is in the vector since i print it in the for loop. However why the iterator of find does not resolve to true?
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{
string m = "valueToSearch was found in the vector " + valueToSearch;
cout << m << endl;
}
are you sure it doesn't work?
I just tried it:
#include<iostream> // std::cout
#include<vector>
#include <algorithm>
using namespace std;
int main()
{
vector<uint8_t> v = { 21 };
uint8_t valueToSearch = 21;
for (vector<uint8_t>::const_iterator i = v.begin(); i != v.end(); ++i){
cout << unsigned(*i) << ' ' << endl;
}
auto it = find(v.begin(), v.end(), valueToSearch);
if ( it != v.end() )
{// if we hit this condition, we found the element
string error = "valueToSearch was found in the vector ";
cout << error << int(valueToSearch) << endl;
}
return 0;
}
There are two small modifications:
in the last lines inside the "if", because you cannot add directly a
number to a string:
string m = "valueToSearch was found in the vector " + valueToSearch;
and it prints:
21
valueToSearch was found in the vector 21
while it's true that you cannot add a number to a string, cout
support the insertion operator (<<) for int types, but not uint8_t,
so you need to convert it to it.
cout << error << int(valueToSearch) << endl;
This to say that the find is working correctly, and it is telling you that it found the number in the first position, and for this, it != end (end is not a valid element, but is a valid iterator that marks the end of your container.)
Try it here
std::count returns a value and I need this value to reset to 0 for all characters in the variable 'counter' after executing the inner for loop. Goal is to count how many times a character appears. If this character appears twice in the string, add one to variable 'd'. If it appears three times, add one to variable 'e'.
Not sure what else to try or if there is potentially a better function to achieve my result.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>
int main() {
std::string data;
std::vector<std::string> myString;
std::vector<char> myChar;
int d = 0, e = 0;
std::ifstream inFile;
inFile.open("C:\\Users\\Administrator\\Desktop\\c++ files\\input2.txt");
if (!inFile) {
std::cout << "oops";
}
for (int i = 0; i < 1; i++) {
inFile >> data;
std::copy(data.begin(), data.end(), std::back_inserter(myChar)); //copy from string data to vector myChar via back inserter.
char counter = 'a';
for (int i = 0; i < 26; i++) {
int myCount = std::count(myChar.begin(), myChar.end(), counter);
if (myCount == 2) {
d++;
}
else if (myCount == 3) {
e++;
}
std::cout << "Counter : " << counter << " myCount : " << myCount << "\n";
counter++;
}
}
std::cout << "d is: " << d << "\n";
std::cout << "e is: " << e << "\n";
return 0;
}
input file -- https://adventofcode.com/2018/day/2
The program works correctly on first inner for loop, but second and after return values that are too high (albeit correct) for the 'myCount' variable.
std::count doesn't just give you a random value, it gives you a specific value based on the contents of the range you give it. You can't change that behaviour, not should you want to.
Instead, look at that range. Why does std::count gives values that you don't expect? They are either "too high" or they are "correct" and cannot be both; fortunately they are the latter.
This is because you repeatedly std::back_insert to the vector inside your loop. As the loop progresses, you keep counting the old characters from the last time!
If you first cleared myChar then you wouldn't have the problem. Or, ideally, bring the declaration of myChar inside the loop.
A few fixes
1) On error the program should end, not continue:
if (!inFile)
{
std::cout << "oops";
return 1;
}
2) a)myChar is accumulating all the chars of all previously read words, so it has to be cleared before use with every pass of the loop, best to move it's declaration into the block required;
b) if you're using a counter just to count but not using it, better to iterate over the data - in this case get rid of i and iterate with chars checked_char:
while (inFile >> data)
{
std::vector< char > myChar;
std::copy(data.begin(),
data.end(),
std::back_inserter(myChar)); //copy from string data to vector myChar via back inserter.
for (char checked_char = 'a'; checked_char <= 'z'; ++checked_char)
{
int myCount = std::count(myChar.begin(), myChar.end(), checked_char);
if (myCount == 2)
{
d++;
}
else if (myCount == 3)
{
e++;
}
std::cout << "Counter : " << checked_char << " myCount : " << myCount << "\n";
}
}
This program takes a word from text and puts it in a vector; after this it compares every element with the next one.
So I'm trying to compare element of a vector like this:
sort(words.begin(), words.end());
int cc = 1;
int compte = 1;
int i;
//browse the vector
for (i = 0; i <= words.size(); i++) { // comparison
if (words[i] == words[cc]) {
compte = compte + 1;
}
else { // displaying the word with comparison
cout << words[i] << " Repeated : " << compte; printf("\n");
compte = 1; cc = i;
}
}
My problem in the bounds: i+1 may exceed the vector borders. How to I handle this case?
You need to pay more attention on the initial conditions and bounds when you do iteration and comparing at the same time. It is usually a good idea to execute your code using pen and paper at first.
sort(words.begin(), words.end()); // make sure !words.empty()
int cc = 0; // index of the word we need to compare.
int compte = 1; // counting of the number of occurrence.
for( size_t i = 1; i < words.size(); ++i ){
// since you already count the first word, now we are at i=1
if( words[i] == words[cc] ){
compte += 1;
}else{
// words[i] is going to be different from words[cc].
cout << words[cc] << " Repeated : " << compte << '\n';
compte = 1;
cc = i;
}
}
// to output the last word with its repeat
cout << words[cc] << " Repeated : " << compte << '\n';
Just for some additional information.
There are better ways to count the number of word appearances.
For example, one can use unordered_map<string,int>.
Hope this help.
C++ uses zero-based indexing, e.g., an array of length 5 has indices: {0, 1, 2, 3, 4}. This means that index 5 is outside of the range.
Similarly, given an array arr of characters:
char arr[] = {'a', 'b', 'c', 'd', 'e'};
The loop for (int i = 0; i <= std::size(arr); ++i) { arr[i]; } will cause a read from outside of the range when i is equal to the length of arr, which causes undefined behaviour. To avoid this the loop must stop before i is equal to the length of the array.
for (std::size_t i = 0; i < std::size(arr); ++i) { arr[i]; }
Also note the use of std::size_t as type of the index counter. This is common practice in C++.
Now, let's finish with an example of how much easier this can be done using the standard library.
std::sort(std::begin(words), std::end(words));
std::map<std::string, std::size_t> counts;
std::for_each(std::begin(words), std::end(words), [&] (const auto& w) { ++counts[w]; });
Output using:
for (auto&& [word, count] : counts) {
std::cout << word << ": " << count << std::endl;
}
My problem in the bounds: i+1 may exceed the vector borders. How to I
handle this case?
In modern C++ coding, the problem of an index going past vector bounds can be avoided. Use the STL containers and avoid using indices. With a little effort devoted to learning how to use containers this way, you should never see these kind of 'off-by-one' errors again! As a benefit, the code becomes more easily understood and maintained.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main() {
// a test vector of words
vector< string > words { "alpha", "gamma", "beta", "gamma" };
// map unique words to their appearance count
map< string, int > mapwordcount;
// loop over words
for( auto& w : words )
{
// insert word into map
auto ret = mapwordcount.insert( pair<string,int>( w, 1 ) );
if( ! ret.second )
{
// word already present
// so increment count
ret.first->second++;
}
}
// loop over map
for( auto& m : mapwordcount )
{
cout << "word '" << m.first << "' appears " << m.second << " times\n";
}
return 0;
}
Produces
word 'alpha' appears 1 times
word 'beta' appears 1 times
word 'gamma' appears 2 times
https://ideone.com/L9VZt6
If some book or person is teaching you to write code full of
for (i = 0; i < ...
then you should run away quickly and learn modern coding elsewhere.
Same repeated words counting using some C++ STL goodies via multiset and upper_bound:
#include <iostream>
#include <vector>
#include <string>
#include <set>
int main()
{
std::vector<std::string> words{ "one", "two", "three", "two", "one" };
std::multiset<std::string> ms(words.begin(), words.end());
for (auto it = ms.begin(), end = ms.end(); it != end; it = ms.upper_bound(*it))
std::cout << *it << " is repeated: " << ms.count(*it) << " times" << std::endl;
return 0;
}
https://ideone.com/tPYw4a
I have a std::set of strings and I want to iterate over them, but the iterator is behaving differently for different sizes of set. Given below is the code snippet that I'm working on:
int test(set<string> &KeywordsDictionary){
int keyword_len = 0;
string word;
set<string>::iterator iter;
cout << "total words in the database : " << KeywordsDictionary.size() << endl;
for(iter=KeywordsDictionary.begin();iter != KeywordsDictionary.end();iter++) {
cout << *iter;
word = *iter;
keyword_len = word.size();
if(keyword_len>0)
Dosomething();
else
cout << "Length of keyword is <= 0" << endl;
}
cout << "exiting test program" << endl;
}
The code is working properly & *iter is being dereferenced & assigned to word until the size of KeywordsDictionary is around 15000. However when the size of KeywordsDictionary increases beyond 15000,
the print statement cout << *iter; is printing all the contents of KeywordsDictionary correctly.
but the pointer to the iterator *iter is not being dereferenced & not being assigned to word. word is just being an empty string.
EDIT: And the output of the program is :
total words in the database : 22771
�z���AAAADAAIIABABBABLEABNABOUTACACCEPTEDACCESSACCOUNT...
Length of keyword is <= 0
exiting test program
So basically, I'm guessing the loop is executing only once.
Try to declare keyword_len as
std::string::size_type keyword_len = 0;
instead of
int keyword_len = 0;
My remove_if seems to be overwriting the elements that are not filtered out with values of filtered out elements. The purpose of these code is to allow user to filter and display only teacher from a certain category. (Not deleting any element)
Here are some of the code
static string compare;
static string debug;
bool filter_Cat (Teacher &t)
{
return (t.getCat() != compare);
}
void filterCat (vector<Teacher> &t)
{
vector<Teacher>::iterator i;
vector<Teacher>::iterator newedited = remove_if(t.begin(), t.end(), filter_Cat);
for (i = t.begin(); i != newedited; ++i)
{
Teacher& te = *i;
te.getName();
cout << "\t";
te.getCategory();
cout << "\t";
te.getLocation();
}
}
void filterTutorCat(vector<Teacher> &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
if(choice <= 3 && choice > 0)
{
if (choice == 1)
{
compare = "English";
filterCat(t);
}
if (choice == 2)
{
compare = "Math";
filterCat(t);
}
if (choice == 3)
{
compare = "Science";
filterCat(t);
}
}
else
{
cout << "Invalid Option" << endl;
}
}
remove_if shifts elements, for which the compare function returns false, from right to left; which in other words means, it overwrites the elements, for which compare returns true, with elements, for which compare returns false. The size of the vector doesn't change, however.
This reads,
Removes all elements satisfying specific criteria from the range [first, last). The first version removes all elements that are equal to value, the second version removes all elements for which predicate p returns true.
Removing is done by shifting the elements in the range in such a way that elements to be erased are overwritten. The elements between the old and the new ends of the range have unspecified values. Iterator to the new end of the range is returned. Relative order of the elements that remain is preserved.
So what you want to do should be expressed as:
void filterCat (vector<Teacher> &v)
{
for (vector<Teacher>::iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter_Cat(*i))
{
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
It seems in your code, getName() prints the name which ideally it should not do, instead it should return name. So I would suggest you to change it to make it return name. And do the same for getCategory as well. Choose your name correctly. If it is getName(), you should get you name by returning it; if it is printName(), then it should print name.
Also, the code which you've written isn't good:
You should avoid global variables.
You should avoid if-else as much as possible. Learn better ways.
You should learn about function objects (or functor)
You should learn about const member function.
You should understand the difference between iterator and const_iterator, and their usage.
You should understand the difference between const reference, and non-const reference. And try using them appropriately.
So I would write your code as:
//this is functor, not a function
struct filter_cat
{
std::string m_cat; //use member data, avoid global variable
filter_cat(std::string const & cat) : m_cat(cat) {}
bool operator()(Teacher const & t) const //const member function
{
return (t.getCat() != m_cat); //getCat should be const member function
}
};
//pass vector by const reference
void filterCat (vector<Teacher> const & v, filter_cat filter)
{
//use const_iterator here, instead of iterator
for (vector<Teacher>::const_iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter(*i))
{
//getName and getCategory should be const member function
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
void filterTutorCat(vector<Teacher> const &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
//avoid if-else as much as possible, learn better ways!
std::string cats[] = {"English", "Math", "Science"};
if(choice <= 3 && choice > 0)
{
filterCat(v, filter_cat(cats[choice-1]));
}
else
{
cout << "Invalid Option" << endl;
}
}
As noted in the comments: getCat, getName and getCategory should be const member functions. In fact, if getCategory returns category, then getCat isn't even needed.
Solved my issue.
remove_if collects the values for which filter_Cat returns false at the start of the container. While it doesn't reduce the number of elements in the container it neither does make any guarantees about the values of the elements beyond the returned range. So you are loosing values when using remove_if.