count of elements in a set (not total count) - c++

I have a set of strings in c++.
i am inserting into that set as :
m.insert("1-2-35-2");
m.insert("1-2-36-1");
m.insert("1-2-37-2");
m.insert("1-2-38-1");
m.insert("1-2-39-2");
m.insert("2-2-40-1");
m.insert("2-2-41-2");
m.insert("2-2-42-1");
m.insert("1-2-43-2");
m.insert("1-2-44-1");
m.insert("1-2-45-2");
m.insert("1-2-46-1");
m.insert("1-2-47-2");
i want to calculate the count of all the strings inside the set which start with "2-"(count =3) and also which start with "1-"(count=10).
is there any way to do it.
I tried with lower_bound and upper_bound but its giving me some errors.
errors are coming for the statement:
int i=it_upper-it_lower;
I am using solaris SPARC OS.
i just tested this program
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
int main () {
list<int> mylist;
for (int i=0; i<10; i++) mylist.push_back (i*10);
list<int>::iterator first = mylist.begin();
list<int>::iterator last = mylist.end();
cout << "The distance is: " << distance(first,last) << endl;
return 0;
}
it gives me compilation error:
line 13: Error: Could not find a match for std::distance<std::ForwardIterator, std::Distance>(std::list<int, std::allocator<int>>::iterator, std::list<int, std::allocator<int>>::iterator).
1 Error(s) detected.

Sorry. Wrong answer
Update:
count_if is an algorithm to count elements based on function. Try like in this example:
bool struct key_part: public std::unary_function< std::string, bool >
{
std::string _part;
key_part(const std::string part):_part(part){}
bool operator()(std::string &s)
{
return s.find(_part)!=std::string::npos;
}
}
std::count_if( m.begin(), m.end(), key_part("1-") );
It will count all elements that contains "1-" as part of key

If you have a modern compiler that supports lambdas, you could use those as the predicate to count_if:
auto if_s_1 = [](const std::string &s) { return s.find("1-") == 0; }
auto if_s_2 = [](const std::string &s) { return s.find("2-") == 0; }
int count1 = std::count_if(m.begin(), m.end(), if_s_1);
int count2 = std::count_if(m.begin(), m.end(), if_s_2);

Related

Appending to map with set<size_t> in it

Hey my mentor(kinda) gave me this task and i am having trouble solving it .
So basically i am getting const vector<string> &data full of string and i need to check their place, like where are they in that vector so here is an example:
getting an input with: data={"chair","desk","table","chair","chair","desk"}
my output should be:{"chair" ->{0,3,4},"desk"->{1,5} , "table"->{2}}
so what i did is :
map<string, set<size_t>> index(const vector<string> & data) noexcept {
map<string, set<size_t>> res; // thats gon be my return
if (data.empty()){
return res;
}
for (auto it = data.begin(); it != data.end(); it++) {
if(res.find(*it)==res.end()){
//so basically when we are the first , and nothing is there so
// it should insert for example =chair , and a 0
res.insert(std::make_pair(*it, 0)); // i tried it like that it doesnt work
}else{
// when the string "chair is already in there i want to append to the set the current index where we are and thats what i dont know how
}
}
return res;
}
how to get the index of the current string and append it to my set<size_t> so that is works as mentioned?
You can implement this quite easily by using an index-based loop:
map<string, set<size_t>> index(const vector<string> & data)
{
map<string, set<size_t>> res;
for (std::size_t i = 0u; i < data.size(); ++i)
res[ data[i] ].insert(i);
// ^string^ ^index
return res;
}
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
std::map<std::string, std::set<size_t>> index(const std::vector<std::string>& data) noexcept
{
std::map<std::string, std::set<size_t>> res;
for (size_t idx = 0u; idx < data.size(); idx++)
{
res[data[idx]].insert(idx);
}
return res;
}
int main()
{
const std::vector<std::string> data={"chair","desk","table","chair","chair","desk"};
auto map = index(data);
for( auto it = map.begin(); it != map.end(); ++it )
{
std::cout << (*it).first << " ->";
for( auto it_id = (*it).second.begin(); it_id != (*it).second.end(); it_id++)
{
std::cout << " " << *it_id;
}
std::cout << "\n";
}
}
So your problem can be solved with a oneline as seen in the index function - but why ?
There is a reason youre getting all these different std container types as input - each of them have some guarantees as can be looked up in the documentation.
vector elements are continous in memory (sequence container).
while map and set are associative and have unique keys.
Consult the documentation for container properties (i linked to the vector - but the rest are also present in the tree to the left).
http://www.cplusplus.com/reference/vector/vector/
The lesson is knowing which tool(container) to use to solve specific tasks
And once you have that nailed .. making sure items are unique or how to sequence every odd element in a vector should be a breeze.

Return the sub string [duplicate]

This question already has answers here:
Frequency of ngrams (strings) in tokenized text
(2 answers)
Closed 3 years ago.
How can I use this for x amounts of numbers ? for my approach, its hard coded to 2 substrings. Also is there a better way with less time complexity? There may be a loop hole over here which needs to be fixed about the number of num im passing as I am not using the um parameter at all.
Your current approach has a few problems, including a hard-coded maximum number of ngrams, and the fixed ngram size. In addition, your short variable names and lack of comments do not help explain the code to whoever is reading it.
A simpler solution is to use a map to count the number of times each ngram occurs, and then find the one with the highest count. That would give rougly N.logN time complexity. Alternatively unordered_map would be closer to linear time complexity.
There will of course be an edge case where more than one ngram occurs the same highest count. You would need to decide which of a variety of strategies should be used to resolve that. In my example, I take advantage of intrinsic ordering of std::map to select the ngram with the lowest sort order. If using unordered_map, you'd need a different strategy for resolving contention in a deterministic way.
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
std::string ngram(const std::string &input, int num)
{
if (num <= 0 || num > input.size()) return "";
// Count ngrams of size 'num'
std::map<std::string, int> ngram_count;
for(size_t i = 0; i <= input.size() - num; i++)
{
++ngram_count[input.substr(i, num)];
}
// Select ngram with highest count
std::map<std::string, int>::iterator highest = std::max_element(
ngram_count.begin(), ngram_count.end(),
[](const std::pair<std::string, int>& a, const std::pair<std::string, int>& b)
{
return a.second < b.second;
});
// Return ngram with highest count, otherwise empty string
return highest != ngram_count.end() ? highest->first : "";
}
int main()
{
std::cout << ngram("engineering", 2) << std::endl;
std::cout << ngram("engineering", 3) << std::endl;
return 0;
}
I did it a bit different than paddy, so I thought I would post it. Use std::set. He explains the issue so should get the credit for your answer.
struct test {
test(const std::string& str) :val(str), cnt(0) {}
test(const test& thet) { *this = thet; }
std::string val;
int cnt;
friend bool operator < (const test& a, const test& b) { return a.val < b.val; }
};
using test_set_type = std::set<test>;
const test ngram(std::string A, int num) {
test_set_type set;
for (auto it = A.begin(); it < A.end() - num + 1; ++it)
{
auto found = set.find(std::string(it, it + num));
if (found != set.end())
++const_cast<test&>(*found).cnt;
else
set.insert(std::string(it, it + num));
}
int find = -1;
test_set_type::iterator high = set.begin();
for (auto it = set.begin(); it != set.end(); ++it)
if(it->cnt > find)
++find, high= it;
return *high;
}
int main() {
int num = 2;
std::string word("engineering");
std::cout << ngram(word, num).val << std::endl;
return 0;
}

How to erase element from std::vector<string> by its length(erase not working)

i have given a vector `
vector<string> inputArray = { "aba","aa","ad","vcd","aba" };
and i want to return this vector which contains only string with the longest length, in this case i want to return only {"aba","vcd","aba"}, so for now i want to erase elements which length is not equal to the highest `
vector<string> allLongestStrings(vector<string> inputArray) {
int length = inputArray.size();
int longstring = inputArray[0].length();
int count = 0;
vector<string> result;
for (int i = 0; i < length; i++)
{
if (longstring < inputArray[i].length())
{
longstring = inputArray[i].length();
}
count++;
}
for (int = 0; i<count;i++)
{
if (inputArray[i].length() != longstring)
{
inputArray[i].erase(inputArray.begin() + i);
count--;
i--;
}
}
return inputArray;
}
but i get this error no instance of overloaded fucntion "std::basic_string<_Elem,_Traits,_Alloc>::erase[with_Elem=char,_Traits=std::char_traits<char>,_Alloc=std::allocator<char>]" matches the argument list" in inputArray[i].erase(inputArray.begin()+i); this line
what's wrong?
There are other problems, but this specific compiler message is telling you that's not the right way to remove specific character(s) from a string.
However, reading the question in the OP, we see that you wanted to remove a string from a vector. To fix that one specific error, simply change
inputArray[i].erase( /*character position(s) in the string*/ )
to
inputArray.erase( /*some position in the array*/ )
Or you could fix it so it uses an iterator in the string denoted by inputArray[i] to actually delete characters from that string, which of course isn't what you said you wanted to do. The point is, the error message is because you're using the wrong iterator type because you think that you're working with a vector, but you actually told it to work with a string that you got out of the vector.
And then you will compile and have other issues which are well covered in comments already.
The issue with inputArray[i].erase(inputArray.begin() + i); can be fixed as shown in Kenny Ostrom's answer.
I'd like to point out that the OP could make use of the erase-remove idiom or even create a new vector with only the bigger strings instead (the posted code is already copying the source vector).
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
template <typename InputIt>
auto only_the_longest_of(InputIt first, InputIt last)
{
using value_type = typename std::iterator_traits<InputIt>::value_type;
std::vector<value_type> result;
// find the longest size
auto longest = std::max_element(first, last,
[](value_type const &a, value_type const &b) {
return a.size() < b.size();
});
if ( longest == last )
return result;
// extract only the longest ones, instead of erasing
std::copy_if( first, last, std::back_inserter(result)
, [max_size = longest->size()] (value_type const& v) {
return v.size() >= max_size;
});
return result;
}
template <typename T>
auto erase_the_shortest_from(std::vector<T> &input)
{
// find the longest size
auto longest = std::max_element(input.cbegin(), input.cend(),
[](T const &a, T const &b) {
return a.size() < b.size();
});
if ( longest == input.cend() || longest->size() == 0 )
return input.end();
// implement erase-remove idiom
return input.erase(std::remove_if(
input.begin(), input.end(), [max_size = longest->size()] (T const &v) {
return v.size() < max_size;
}));
}
int main()
{
std::vector<std::string> test = {
"aba", "aa", "ad", "vcd", "aba"
};
// The original vector remain unchanged
auto result = only_the_longest_of(test.cbegin(), test.cend());
for (auto const& str : result)
std::cout << str << '\n';
std::cout << '\n';
// This will change the vector
erase_the_shortest_from(test);
for (auto const& str : test)
std::cout << str << '\n';
}

How to apply the concept of counting occurrences on strings variables in C++

following program ca calculate the frequency of ints in an array
how to apply this concept on string variable because a string is also an array on the back end
using namespace std;
int counter[10]={0,0,0,0,0,0,0,0,0,0};
int arr [9][9],x;
int main()
{
srand(time(NULL));
cout<<"enter the array \n";
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
arr[i][j]=rand()%10;
}
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
counter[arr[i][j]]++;
}
}
for(int j=0;j<10;j++){
cout<<j<<" : "<< counter[j]<<endl;
}
return 0;
}
Here is how one can count occurrences of anything from anything:
Code
#include <iterator>
#include <map>
#include <algorithm>
template<class InputIt>
auto
occurrences(InputIt begin, InputIt end)
{
std::map<typename std::iterator_traits<InputIt>::value_type, std::size_t> result;
std::for_each(begin, end, [&result](auto const& item){ ++result[item]; });
return result;
}
Usage
#include <string>
#include <iostream>
int main()
{
auto text = std::string{"Hello, World!"};
auto occ = occurrences(begin(text), end(text));
std::cout << occ['l'] << '\n'; // outputs 3
}
Live demo
Explanation
template<class InputIt>
This is a generic (template) function iterating over any input iterator.
auto
Its return type is inferred from its implementation. Spoiler alert: it is a std::map of (value counter, occurrence of this value).
occurrences(InputIt begin, InputIt end)
occurrences is called with a couple of iterators defining a range, generally calling begin(C) and end(C) on your container C.
std::for_each(begin, end, //...
For each element in the range...
[&result](auto const& item){ //...
...execute the following treatment...
++result[item]; });
...increment the occurrence count for the value item, starting with zero if its the first.
This is not an efficient implementation since it copies the values it counts. For integers, characters, etc. its perfect but for complex types you might want to improve this implementation.
It's generic and standard container compatible. You could count anything iterable.
If I understand correctly, you want to count occurrences of strings. STL container map is useful for this purpose. Following is example code.
#include<iostream>
#include<map>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> arrayString;
std::map<std::string, int> counter;
std::map<std::string, int>::iterator it;
arrayString.push_back("Hello");
arrayString.push_back("World");
arrayString.push_back("Hello");
arrayString.push_back("Around");
arrayString.push_back("the");
arrayString.push_back("World");
// Counting logic
for(std::string strVal : arrayString)
{
it = counter.find(strVal);
if(it != counter.end())
it->second += 1; // increment count
else
counter.insert(std::pair<std::string, int>(strVal, 1)); // first occurrence
}
// Results
for(std::map<std::string, int>::iterator it = counter.begin(); it != counter.end(); ++it)
std::cout << it->first << ": " << it->second << std::endl;
return 0;
}
More compact way to write the counting logic is :
// Counting logic
for(std::string strVal : arrayString)
{
++counter[strVal]; // first time -> init to 0 and increment
}

How to get indices of a Mat with specific value?

I want to find indices of an array that equal with specific value. so i've Written this code:
vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
vector<int> labelVec = {1,2,3,4,5,6};
vector<int> index;
for (int i = 0; i < labelVec.size(); i++)
{
compare(_classes, labelVec[i], index, CMP_EQ);
std::vector<int>::iterator nn = find(index.begin(), index.end(), 255);
}
but i have this error : Unhandled exception at 0x760B5608 in compareFuncTest.exe: Microsoft C++ exception: cv::Exception at memory location 0x004DDC44. if i define index as Mat, this problem will be resolved. but if i define index as Mat, i can't use from find(). also in this documentation states: output array (in my code as index) that has the same size and type as the input arrays. PLZ help me to fix this code.
I still do not get what is the point of this test, I guess this will be in some other algorithm... So, I give you two possible solutions.
1) Without OpenCV
First, you must know that
std::vector<int>::iterator nn = find(index.begin(), index.end(), 255);
Will only give you the first occurrance. Knowing this, here is a way you could check if the label is inside the _classes vector.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
std::vector<int> labelVec = {1,2,3,4,5,6,7};
for (const auto& label: labelVec)
{
std::vector<int>::iterator nn = find(_classes.begin(), _classes.end(), label);
if (nn != _classes.end())
{
std::cout << "I got the value from _classes: " << *nn << std::endl;
} else
{
std::cout << "I couldn't find the value with label:" << label << std::endl;
}
}
}
Here I iterate over all the labels (as you did) and then use the find directly in the classes, but with the label variable. Then I check if I found the label or not, if not, it will give you a value equal to _classes.end() which will give error if you try to use it (look at the extra label 7 which is not found).
This example can be tested here online.
2) With OpenCV
no oline test here. But this one is also easy to do. If you have a Mat in index you will only need to change the iterators to be templated. Like this:
auto nn = find(index.begin<int>(), index.end<int>(), 255);
If you a cv::Mat of classes you can also do it as in the method before and skip the comparison part (this would be faster)
Update
Since you want is the indices and all of them, then you have to iterate over it :/ if you wanted the values you could have used copy_if. You can create a lambda function to easily do the job.
like this:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
auto getIndices = [](const std::vector<int>& vec, const int value){
std::vector<int> result;
for (size_t t = 0; t < vec.size(); t++)
{
if (vec[t] == value)
{
result.push_back(static_cast<int>(t));
}
}
return result;
};
std::vector<int> _classes = { 2,2,1,1,3,3,3,3,5,5,4,4,5,6,6 };
std::vector<int> labelVec = {1,2,3,4,5,6,7};
for (const auto& label: labelVec)
{
std::vector<int> nn = getIndices(_classes, label);
std::cout << "I got the following indices for value"<< label<< ": [ ";
for (const auto& n : nn)
{
std::cout << n << ",";
}
std::cout << " ]" << std::endl;
}
}