I would like to getting the first position and last position of the searched name.
I can't compile this code, although I've seen similar instructions being executed. Gives an error in lower_bound and upper_bound.
Compiled in C++11
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
class Client
{
public:
int id;
string name;
int number;
};
int main()
{
vector<Client>::iterator low;
vector<Client>::iterator up;
string s_name;
Client c1;
c1.id = 1;
c1.name = "jhon";
c1.number = 123;
Client c2;
c2.id = 2;
c2.name = "Mart";
c2.number = 987;
Client c3;
c3.id = 3;
c3.name = "Jhon";
c3.number = 256;
Client c4;
c4.id = 4;
c4.name = "Anna";
c4.number = 851;
vector<Client> vCli{c1, c2, c3, c4};
sort(vCli.begin(), vCli.end(), [](Client a, Client b) { return a.name < b.name; });
s_name = "Jhon";
low = lower_bound(vCli.begin(), vCli.end(), s_name, [](Client a, Client b) { return a.name < b.name; });
up = upper_bound(vCli.begin(), vCli.end(), s_name, [](Client a, Client b) { return a.name < b.name; });
cout << (low - vCli.begin()) << endl;
cout << (up - vCli.begin()) << endl;
return 0;
}
C:\Program Files (x86)\CodeBlocks\MinGW\lib\gcc\mingw32\5.1.0\include\c++\bits\predefined_ops.h|144|
error: no match for call to '(main()::<lambda(Client, Client)>) (Client&, const std::__cxx11::basic_string<char>&)'|
The third argument to std::lower_bound and std::upper_bound has to be a Client object or something that can be converted to a Client. If you add a constructor in Client that allows you to construct a Client from a std::string implicitly, your code would work. Here's a quick fix that does not require any other changes to your code.
Client s;
s.name = "Jhon";
low = lower_bound (vCli.begin(), vCli.end(), s, [](Client a, Client b) { return a.name < b.name; });
up = upper_bound (vCli.begin(), vCli.end(), s, [](Client a, Client b) { return a.name < b.name; });
As you are searching for a std::string one argument in your comparator needs to be a std::string not a Client:
low = lower_bound(vCli.begin(), vCli.end(), s_name, [](const Client& a, const std::string& b) { return a.name < b; });
up = upper_bound(vCli.begin(), vCli.end(), s_name, [](const std::string& a, const Client& b) { return a < b.name; });
Here you are.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
class Client
{
public:
int id;
std::string name;
int number;
};
int main()
{
std::vector<Client> vCli =
{
{ 1, "Jhon", 123 },
{ 2, "Mart", 987 },
{ 3, "Jhon", 256 },
{ 4, "Anna", 851 },
};
std::sort( std::begin( vCli ), std::end( vCli ),
[]( const Client &c1, const Client &c2 )
{
return c1.name < c2.name;
} );
std::string s_name = "Jhon";
auto low = std::lower_bound( std::begin( vCli ), std::end( vCli ), s_name,
[]( const Client &c, const std::string &s )
{
return c.name < s;
} );
auto up = std::upper_bound( std::begin( vCli ), std::end( vCli ), s_name,
[]( const std::string &s, const Client &c )
{
return s < c.name;
} );
for ( auto first = low; first != up; ++first )
{
std::cout << first->id << ", "
<< first->name << ", "
<< first->number << '\n';
}
return 0;
}
The program output is
1, Jhon, 123
3, Jhon, 256
Instead of separate calls of std::lower_bound and std::upper_bound you could use one call of std::equal_range. In this case you should define a function object as shown in the demonstrative program below
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
class Client
{
public:
int id;
std::string name;
int number;
};
struct Compare_by_name
{
bool operator ()( const Client &c, const std::string &s ) const
{
return c.name < s;
}
bool operator ()( const std::string &s, const Client &c ) const
{
return s < c.name;
}
};
int main()
{
std::vector<Client> vCli =
{
{ 1, "Jhon", 123 },
{ 2, "Mart", 987 },
{ 3, "Jhon", 256 },
{ 4, "Anna", 851 },
};
std::sort( std::begin( vCli ), std::end( vCli ),
[]( const Client &c1, const Client &c2 )
{
return c1.name < c2.name;
} );
std::string s_name = "Jhon";
auto low_up = std::equal_range( std::begin( vCli ), std::end( vCli ), s_name,
Compare_by_name() );
for ( auto first = low_up.first; first != low_up.second; ++first )
{
std::cout << first->id << ", "
<< first->name << ", "
<< first->number << '\n';
}
return 0;
}
This function object you can use also with std::lower_bound and std::upper_bound instead of their lambda expressions.
Related
I have a cpp vector containing separate words and I need to count how many times a word appears using a list. I try to iterate through the list but failing with the comparison of the two STL containers, whether the following word is already in my list or not. If not, I want to add that word to my list with an appearance of 1. I have a struct that counts the times a word appeared in the text.
The following code returns a list of words and numbers, but not each in my vector and I can't see why.
struct counter{
string word;
int sum = 1;
counter(){};
counter(string word): word(word){};
};
list<counter> list_count(vector<string> &text){
list<counter> word_count;
list<counter>::iterator it = word_count.begin();
for(string t:text){
if(it != word_count.end()){
it -> sum++;
} else {
word_count.push_back(counter(t));
}
++it;
}
return word_count;
}
Thank you in advance.
list<counter> list_count(const vector<string>& text) {
list<counter> word_count;
for (const string& t : text) {
auto it = std::find_if(word_count.begin(), word_count.end(),
[&](const counter& c){ return c.word == t; });
if (it != word_count.end()) {
it -> sum++;
} else {
word_count.push_back(counter(t));
}
}
return word_count;
}
Untested code.
You are not actually searching the std::list at all. On every loop iteration through the std::vector, you need to search the entire std::list from front to back, eg:
#include <string>
#include <list>
#include <vector>
#include <algorithm>
using namespace std;
struct counter {
string word;
int sum = 1;
counter(const string &word): word(word) {}
};
list<counter> list_count(const vector<string> &text) {
list<counter> word_count;
for(const string &t: text) {
// perform an actual search here!
list<counter>::iterator it = find_if(
word_count.begin(), word_count.end(),
[&](counter &c){ return (c.word == t); }
);
if (it != word_count.end()) {
it->sum++;
} else {
word_count.emplace_back(t);
}
}
return word_count;
}
Live Demo
That being said, a std::list is a poor solution for counting elements. A better solution is to use a std::(unordered_)map instead (unless you need to preserve the order of the words found, which neither one will do), eg:
#include <string>
#include <map>
#include <vector>
using namespace std;
map<string, int> list_count(const vector<string> &text) {
map<string, int> word_count;
for(const string &t: text) {
word_count[t]++;
}
return word_count;
}
Live Demo (using std::map)
Live Demo (using std::unordered_map)
You are trying to use an inefficient approach. The standard class template list does not have random access to its elements. Each new element is appended to the end of the list. To find whether an element is already present in the list elements of it are traversed sequentially.
It would be much efficiently to use the standard container std::map . Moreover in this container words will be ordered.
For example you could declare
std::map<std::string, size_t> counters;
Nevertheless if you want to use the list then the function can look as it is shown in the demonstrative program below.
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &c ) { return c.word == s; } );
if ( it == std::end( word_count ) )
{
word_count.push_back( s );
}
else
{
++it->n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
first: 2
second: 1
Pay attention to that the definition of the struct counter is redundant. You could use instead the standard class std::pair. Here you are.
#include <iostream>
#include <string>
#include <utility>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
std::list<std::pair<std::string, size_t>> list_count( const std::vector<std::string> &text )
{
std::list<std::pair<std::string, size_t>> word_count;
for ( const auto &s : text )
{
auto it = std::find_if( std::begin( word_count ), std::end( word_count ),
[&s]( const auto &p ) { return p.first == s; } );
if ( it == std::end( word_count ) )
{
word_count.emplace_back( s, 1 );
}
else
{
++it->second;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
If to use std::map then the function will look very simple.
#include <iostream>
#include <string>
#include <vector>
#include <map>
std::map<std::string, size_t> list_count( const std::vector<std::string> &text )
{
std::map<std::string, size_t> word_count;
for ( const auto &s : text )
{
++word_count[s];
}
return word_count;
}
int main()
{
std::vector<std::string> v { "first", "second", "first" };
auto word_count = list_count( v );
for ( const auto &p : word_count )
{
std::cout << p.first << ": " << p.second << '\n';
}
return 0;
}
Using of the list will be efficient only in the case when the vector of strings is sorted.
Here is a demonstrative program.
#include <iostream>
#include <string>
#include <list>
#include <vector>
struct counter
{
std::string word;
size_t n = 0;
counter() = default;
counter( const std::string &word ): word( word ), n( 1 ){}
};
std::list<counter> list_count( const std::vector<std::string> &text )
{
std::list<counter> word_count;
for ( const auto &s : text )
{
if ( word_count.empty() || word_count.back().word != s )
{
word_count.push_back( s );
}
else
{
++word_count.back().n;
}
}
return word_count;
}
int main()
{
std::vector<std::string> v { "A", "B", "B", "C", "C", "C", "D", "D", "E" };
auto word_count = list_count( v );
for ( const auto &c : word_count )
{
std::cout << c.word << ": " << c.n << '\n';
}
return 0;
}
Its output is
A: 1
B: 2
C: 3
D: 2
E: 1
Could we replace a loop with a strcmp by something easier to read, something similar to the C# extension methods?
I maintain legacy C++ and wonder how to start a transition to a more modern C++ and there is profusion of code that looks like this one:
int numberOfPipe = 10;
char* collection[5] = { "pompe","pipe","turbine","pompe", "pipe" };
// Count the pipes in the collection
int nPipeFound = 0;
int nPipe = 5;
for (int idx = 0; idx < nPipe; idx++)
{
if (strcmp(collection[idx], "pipe") == 0)
nPipeFound++;
}
cout << nPipeFound << endl;
Use the standard library:
Use std::count and use the std::string comparison.
#include <algorithm>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count( std::begin( collection ), std::end( collection ), std::string{"pipe"});
std::cout << n_pipe_found << '\n';
}
Use std::count_if and write a predicate which does C string comparison for you.
#include <algorithm>
#include <cstring>
#include <iostream>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::count_if( std::begin( collection ), std::end( collection ),
[](char const * a) { return std::strcmp(a,"pipe") == 0; } );
std::cout << n_pipe_found << '\n';
}
You could also use a predicate like [](std::string const& a) { return a == "pipe"; } and again make use of std::string comparison.
Use std::accumulate if you need more fine grained control over counting.
#include <numeric>
#include <iostream>
#include <string>
int main() {
char const * collection[] = { "pompe","pipe","turbine","pompe","pipe" };
auto n_pipe_found = std::accumulate( std::begin( collection ), std::end( collection ), int{0},
[](int a, std::string const& b) { return a + (b == "pipe"); });
std::cout << n_pipe_found << '\n';
}
I have the following piece of code. The code creates a vector Dataset, each element of which is a vector. It also creates a vector S.
I want to check which vector of Dataset contain vector of S. Apparently I am doing something wrong, because for the following example,
Dataset is:
a b c
a d
a b d
and S:
a b
it should print: 0 2
and for me it prints: 0 1 2
#include <iostream>
#include <fstream>
#include <sstream>
#include <string.h>
#include <string>
#include <time.h>
#include <vector>
#include <algorithm>
using namespace std;
class StringRef
{
private:
char const* begin_;
int size_;
public:
int size() const { return size_; }
char const* begin() const { return begin_; }
char const* end() const { return begin_ + size_; }
StringRef( char const* const begin, int const size )
: begin_( begin )
, size_( size )
{}
bool operator<(const StringRef& obj) const
{
return (strcmp(begin(),obj.begin()) > 0 );
}
};
/************************************************
* Checks if vector B is subset of vector A *
************************************************/
bool isSubset(std::vector<StringRef> A, std::vector<StringRef> B)
{
std::sort(A.begin(), A.end());
std::sort(B.begin(), B.end());
return std::includes(A.begin(), A.end(), B.begin(), B.end());
}
vector<StringRef> split3( string const& str, char delimiter = ' ' )
{
vector<StringRef> result;
enum State { inSpace, inToken };
State state = inSpace;
char const* pTokenBegin = 0; // Init to satisfy compiler.
for(auto it = str.begin(); it != str.end(); ++it )
{
State const newState = (*it == delimiter? inSpace : inToken);
if( newState != state )
{
switch( newState )
{
case inSpace:
result.push_back( StringRef( pTokenBegin, &*it - pTokenBegin ) );
break;
case inToken:
pTokenBegin = &*it;
}
}
state = newState;
}
if( state == inToken )
{
result.push_back( StringRef( pTokenBegin, &str.back() - pTokenBegin ) );
}
return result;
}
int main() {
vector<vector<StringRef> > Dataset;
vector<vector<StringRef> > S;
ifstream input("test.dat");
long count = 0;
int sec, lps;
time_t start = time(NULL);
cin.sync_with_stdio(false); //disable synchronous IO
for( string line; getline( input, line ); )
{
Dataset.push_back(split3( line ));
count++;
};
input.close();
input.clear();
input.open("subs.dat");
for( string line; getline( input, line ); )
{
S.push_back(split3( line ));
};
for ( std::vector<std::vector<StringRef> >::size_type i = 0; i < S.size(); i++ )
{
for(std::vector<std::vector<StringRef> >::size_type j=0; j<Dataset.size();j++)
{
if (isSubset(Dataset[j], S[i]))
{
cout << j << " ";
}
}
}
sec = (int) time(NULL) - start;
cerr << "C++ : Saw " << count << " lines in " << sec << " seconds." ;
if (sec > 0) {
lps = count / sec;
cerr << " Crunch speed: " << lps << endl;
} else
cerr << endl;
return 0;
}
Your StringRef type is dangerous because it contains a const char * pointer, but no concept of ownership. So the pointer could be invalidated at some point after the object is constructed.
And indeed this is what happens here: You have a single string (line) and create StringRefs with pointers to its internal data. When the string is later modified, these pointers are invalidated.
You should create a vector<std::string> instead to prevent this problem.
Consider the following vector
vector<vector<string>> a_words(80000,vector<string>(3));
which is a three dimension vector;
Now consider the following elements:
Joan Williams 30
Mike Williams 40
Joan Smith 30
William Anderson 20
Sara Jon 33
Basically I want to search by row, and I want to find Joan Williams, keep in mind that Joan is an element in the first column and Williams is an element is the second column
Should I use the "find" function? if yes how would it be written, else which function should I use?
Here are two demonstrative programs one for C++ 2003 and other for C++ 2011 that do the search
C++ 2003
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <functional>
struct FindName : std::unary_function<bool,
const std::pair<std::string, std::string>>
{
FindName( const std::pair<std::string, std::string> &p ) : p( p ){}
bool operator ()( const std::vector<std::string> &v ) const
{
return v.size() > 1 &&
v[0] == p.first && v[1] == p.second;
}
protected:
const std::pair<std::string, std::string> p;
};
int main()
{
const size_t N = 5;
std::vector<std::vector<std::string>> v;
v.reserve( N );
const char * initial[N][3] =
{
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" }
};
for ( size_t i = 0; i < N; i++ )
{
v.push_back( std::vector<std::string>( initial[i], initial[i] + 3 ) );
}
std::pair<std::string, std::string> p( "Joan", "Williams" );
typedef std::vector<std::vector<std::string>>::iterator iterator;
iterator it = std::find_if( v.begin(), v.end(), FindName( p ) );
if ( it != v.end() )
{
for ( std::vector<std::string>::size_type i = 0; i < it->size(); ++i )
{
std::cout << ( *it )[i] << ' ';
}
}
std::cout << std::endl;
}
C++ 2011
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
int main()
{
std::vector<std::vector<std::string>> v =
{
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" }
};
std::pair<std::string, std::string> p( "Joan", "Williams" );
auto it = std::find_if( v.begin(), v.end(),
[&]( const std::vector<std::string> &row )
{
return row.size() > 1 &&
row[0] == p.first && row[1] == p.second;
} );
if ( it != v.end() )
{
for ( const auto &s : *it ) std::cout << s << ' ';
}
std::cout << std::endl;
}
The both programs' putput is
Joan Williams 30
I strongly advise you to use a data structure with an overloaded equality operator instead of vector<string> (especially since it seems like the third element should be saved in an integer, not a string).
Anyway, this is one possibility:
auto iter = std::find_if( std::begin(a_words), std::end(a_words),
[] (std::vector<std::string> const& vec)
{ return vec[0] == "Joan" && vec[1] == "Williams";};
If the list is lexicographically sorted by the first or second column, a binary search can be used instead.
As of C++11, a range based for loop would be a simple and readable solution:
for(auto r: a_words)
if(r[0] == "Joan" && r[1] == "Williams")
cout << r[0] << " " << r[1] << " " << r[2] << endl;
Essentially the answer of #Columbo is nice, eliminating C++ 11 features (besides initialization):
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main() {
// Requires C++11
std::vector<std::vector<std::string>> words = {
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" },
};
// Below does not require C++11
struct EqualName
{
const char* first;
const char* second;
EqualName(const char* first, const char* second)
: first(first), second(second)
{}
bool operator () (const std::vector<std::string>& element) {
return element[0] == first && element[1] == second;
}
};
std::vector<std::vector<std::string>>::const_iterator
pos = std::find_if(words.begin(), words.end(), EqualName("Joan", "Smith"));
if(pos != words.end())
std::cout << (*pos)[0] << ' ' << (*pos)[1] << ' ' << (*pos)[2] << '\n';
}
I have a dynamic array which contains a contact number and name. I was wondering how to do a binary search for the name. Let's say I have 20 contacts and I want to find the number of the contact with name "John".
Here is the data structure:
struct Contact
{
int ContactNumber,Fax;
string Name, Email;
PhoneNumber Phone;
Address anAddress;
};
I have:
Contact * ptrFirst = & arrofCont[0];
Contact * ptrLast = & arrofCont[MAX - 1];
that contains the contact name and number and address etc. I guess those can be used as a first and last but don't know where to go from there.
You don't need to sort or binary search your array to do what you want.
Just use std::find_if.
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
struct Company
{
std::string name ;
std::string number ;
};
struct HasName
{
HasName (const std::string &name) : name (name) {}
bool operator () (const Company &company) {
return company.name == name ;
}
std::string name ;
};
int main (void)
{
std::vector <Company> companies ;
// Fill up the vector...
std::vector <Company>::const_iterator citer ;
citer = std::find_if (companies.cbegin (), companies.cend (), HasName ("John")) ;
if (citer != companies.cend ()) {
std::cout << citer->number << "\n" ;
}
return 0 ;
}
Here is an example that uses a lambda expression to compare a pair of elements of the array
It corresponds to your original post before you updated it.
#include <iostream>
#include <algorithm>
#include <string>
struct Contact
{
std::string name;
int number;
};
int main()
{
const size_t N = 3; // or N = 20 or you can use name MAX instead of N
Contact *p = new Contact[N] { { "B", 2 }, { "A", 1 }, { "C", 3 } };
auto less_by_name = []( const Contact &c1, const Contact &c2 )
{
return ( c1.name < c2.name );
};
std::sort( p, p + N, less_by_name);
auto it = std::lower_bound( p, p + N, Contact( { "B", 0 } ), less_by_name );
if ( it != p + N ) std::cout << "The number of \"B\" is " << it->number << std::endl;
delete []p;
}
Or you can make the functor as a member of your class. For example
#include <iostream>
#include <algorithm>
#include <string>
struct Contact
{
std::string name;
int number;
static bool less_by_name( const Contact &c1, const Contact &c2 )
{
return ( c1.name < c2.name );
}
};
int main()
{
const size_t N = 3; // or N = 20 or you can use name MAX instead of N
Contact *p = new Contact[N] { { "B", 2 }, { "A", 1 }, { "C", 3 } };
std::sort( p, p + N, Contact::less_by_name);
auto it = std::lower_bound( p, p + N, Contact( { "B", 0 } ), Contact::less_by_name );
if ( it != p + N ) std::cout << "The number of \"B\" is " << it->number << std::endl;
delete []p;
}
As for your definitions
Contact * ptrFirst = & arrofCont[0];
Contact * ptrLast = & arrofCont[MAX - 1];
then if you will change them the following way
Contact * ptrFirst = arrofCont;
Contact * ptrLast = arrofCont + MAX;
then they will correspond to
Contact * ptrFirst = p;
Contact * ptrLast = p + N;
relative to my examples of code.