Take user input and search through a 2D array - c++

I'm trying to make a program that takes in a string from the user, searches through a 2D array and if it matches a string in the array, print the entire row. So basically, if the user types in the name Bobby G, I'd like it to output Bobby G: ugly and stupid, if input is Billy it outputs Billy: bad, so on and so forth. Below is what I have so far. A bit of an explanation would be greatly appreciated.
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
//!! It only works with letters of the alphabet, so if I replaced Bobby G with the
//letter "A" It would output "ugly, and stupid", and then replace "Billy Smalls" with
//the letter "B" I'd get "Bad" so on and so forth, but I need it to work with the exact
// string, so user input of "Bobby G" outputs "ugly, and stupid"
std::string name[9][2] = {
{"Bobby G","ugly, and stupid"},
{"Billy","bad"},
{"John","smart and cool"},
{"Adam","amzing and beautiful"},
{"Bill","perfect"},
{"Turner","funny"},
{"Sonny","nice"},
{"Jack","radical"},
{"Frank","nice"}};
typedef std::string Full[2];
Full* last_Full = name + sizeof(name) / sizeof(Full);
struct Less {
bool operator () (const Full& a, const string& b) const
{
return a[0] < b;
}
};
std::string input;
std::cin >> input;
Less less_full;
Full* full = std::lower_bound(name, last_Full, input, less_full);
if(full == last_Full || (*full)[0] != input)
std::cout << "Not found" << std::endl;
else std::cout << (*full)[0] << ": " << (*full)[1] << std::endl;
system("Pause");
return 0;
}
I'd want to do it without nesting if statements and making it a mess.

I'm really having a hard time understanding what you wrote in your code but according to the description this should go something like (demo):
#include <iostream>
#include <unordered_map>
#include <string>
int main(void) {
std::unordered_map<std::string, std::string> name = {
{"Bobby G","ugly, and stupid"},
{"Billy","bad"},
{"John","smart and cool"},
{"Adam","amzing and beautiful"},
{"Bill","perfect"},
{"Turner","funny"},
{"Sonny","nice"},
{"Jack","radical"},
{"Frank","nice"}};
std::string in;
std::getline(std::cin,in);
if(name.count(in)){
std::cout << in << " " << name[in] << std::endl;
}
return 0;
}
And to save us headache in the future indent your code and don't make it look like ASCII art...
So basically what we use is an unordered_map which holds the names as keys and the sentences as values.
Then we use cin to receive input from the user and place it into string in.
The last step is to check whether we have such a string as a key in the map using count which will return 1 iff it contains this key.
But seriously, you have to do your reading more seriously; find a tutorial and/or a book and get your concepts straight.

The task is not so simple as it seems. So I up-voted your question.
First of all it is better to use a one-dimensional array of type std::pair<std::string, std::string> instead of the two-dimensional array that you use.
Secondly that to apply algorithm std::lower_bound the array must be sorted.
The code could look the following way
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
{ "Bobby G", "ugly, and stupid" },
{ "Billy", "bad" },
{ "John", "smart and cool" },
{ "Adam", "amzing and beautiful" },
{ "Bill", "perfect" },
{ "Turner", "funny" },
{ "Sonny", "nice" },
{ "Jack", "radical" },
{ "Frank", "nice" }
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
auto p = std::make_pair( std::string( "bobby g" ), std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
return 0;
}
The output is
Bobby G ugly, and stupid
it is about those participants who down voted your question.:)
If you compiler issues an error relative to array initializers you should substitute them the following way. For example
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
Here is code that uses other kind of initializers
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
std::pair<std::string, std::string>( "Billy", "bad" ),
std::pair<std::string, std::string>( "John", "smart and cool" ),
std::pair<std::string, std::string>( "Adam", "amzing and beautiful" ),
std::pair<std::string, std::string>( "Bill", "perfect" ),
std::pair<std::string, std::string>( "Turner", "funny" ),
std::pair<std::string, std::string>( "Sonny", "nice" ),
std::pair<std::string, std::string>( "Jack", "radical" ),
std::pair<std::string, std::string>( "Frank", "nice" )
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
auto p = std::make_pair( std::string( "bobby g" ), std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
return 0;
}
Instead of using long type name std::pair<std::string, std::string> you can introduce some typedef name as for example
typedef std::pair<std::string, std::string> Pair;
Here is the program that asks the user to enter a name
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <cctype>
int main()
{
std::pair<std::string, std::string> name[] =
{
std::pair<std::string, std::string>( "Bobby G", "ugly, and stupid" ),
std::pair<std::string, std::string>( "Billy", "bad" ),
std::pair<std::string, std::string>( "John", "smart and cool" ),
std::pair<std::string, std::string>( "Adam", "amzing and beautiful" ),
std::pair<std::string, std::string>( "Bill", "perfect" ),
std::pair<std::string, std::string>( "Turner", "funny" ),
std::pair<std::string, std::string>( "Sonny", "nice" ),
std::pair<std::string, std::string>( "Jack", "radical" ),
std::pair<std::string, std::string>( "Frank", "nice" )
};
std::sort( std::begin( name ), std::end( name ) );
auto compare_by_name =
[]( const std::pair<std::string, std::string> &p1,
const std::pair<std::string, std::string> &p2 )
{
return std::lexicographical_compare(
p1.first.begin(), p1.first.end(),
p2.first.begin(), p2.first.end(),
[]( char c1, char c2 )
{ return std::toupper( c1 ) < std::toupper( c2 ); } );
};
while ( true )
{
std::cout << "Enter name: ";
std:: string s;
std::getline( std::cin, s );
if ( s.empty() ) break;
auto p = std::make_pair( s, std::string( "" ) );
auto it = std::equal_range( std::begin( name ), std::end( name ), p,
compare_by_name );
if ( it.first != it.second )
{
std::cout << it.first->first + ' ' + it.first->second << std::endl;
}
else
{
std::cout << "The name is not found" << std::endl;
}
}
return 0;
}
Good luck.

Related

how to add lambda function or perform custom operation in STL set in c++

I need a set arranges the value in such a way that if the int values are different i need the lexographically greater string to come front else i want the smaller integer to come front
set<pair<int,string>,[&](auto &a,auto &b){
if(a.first==b.first)return a.second>b.second;
return a.first<b.first;
}>;
It seems you mean the following
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <tuple>
int main()
{
auto less = []( const auto &p1, const auto &p2 )
{
return std::tie( p1.first, p2.second ) <
std::tie( p2.first, p1.second );
};
std::set<std::pair<int, std::string>, decltype( less )>
s( { { 1, "A" }, { 1, "B" }, { 2, "A" } }, less );
for ( const auto &p : s )
{
std::cout << p.first << ' ' << p.second << '\n';
}
}
The program output is
1 B
1 A
2 A
You could use also the constructor without the initializer list
std::set<std::pair<int, std::string>, decltype( less )>
s( less );

Counting the appearance of words in a vector and listing those in a list, C++

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

Deleting empty elements from vector

I am trying to delete empty entries from std::vector. Here is a sample code, but something is wrong here.
#include <iostream>
#include <string>
#include<vector>
#include <cctype>
int main()
{
std::vector<std::string> s1 = {"a"," ", "", "b","c"," ","d"};
for (auto it = s1.begin(); it != s1.end() && isspace(*it); )
{
it = s1.erase(it);
}
std::cout<<"vector size = "<<s1.size();
for (auto &i:s1)
std::cout<<i<<"\n";
}
I am running a for loop to find out empty elements and deleting from there. There should be STL method too, but not sure how it will work.
It seems you mean the following
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::string> v = { "a", " ", "", "b", "c", " ", "d" };
auto is_empty = []( const std::string &s )
{
return s.find_first_not_of( " \t" ) == std::string::npos;
};
v.erase( std::remove_if( std::begin( v ), std::end( v ), is_empty ), std::end( v ) );
for ( const auto &s : v )
{
std::cout << "\"" << s << "\" ";
}
std::cout << std::endl;
return 0;
}
The program output is
"a" "b" "c" "d"
As for your code then it is inefficient because you are trying to remove each found element separately and this loop for example
for (auto it = s1.begin(); it != s1.end() && isspace(*it); )
{
it = s1.erase(it);
}
can iterate never because the first element is not satisfies the condition isspace(*it) that moreover is invalid. That is you are supplying an object of the type std::string to a function that expects an object of the type char (more precisely of the type int).
If to use the C function isspace then the program can look the following way.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <cctype>
int main()
{
std::vector<std::string> v = { "a", " ", "", "b", "c", " ", "d" };
auto is_empty = []( const std::string &s )
{
return std::all_of( std::begin( s ), std::end( s ),
[]( char c )
{
return std::isspace( ( unsigned char )c );
} );
};
v.erase( std::remove_if( std::begin( v ), std::end( v ), is_empty ), std::end( v ) );
for ( const auto &s : v )
{
std::cout << "\"" << s << "\" ";
}
std::cout << std::endl;
return 0;
}
The program output is the same as shown above.

Find an element in vector<vector<string>>

I store this file in vector<vector<string>>:
1 a aa # vector of string stored to `vector<vector<string>>`
2 b bb
3 c cc # c -> index == 2
4 d dd
C++ code:
vector<vector<string>> myvect =
{{"1","a","aa"},
{"2","b","bb"},
{"3","c","cc"},
{"4","d","dd"}};
How can I search for c in the second column and get its index (I know it is in the second vector) - the output should be 2.
I want to use find or find_if function.
If you specificially want to search the 2nd column of the inner vector you can use a transform_iterator and regular find.
transform_iterator is in boost would look something like:
std::vector< std::vector< std::string > > v;
auto lambda = [] ( std::vector< std::string > const& v ) { return v[1]; };
auto transform_end = boost::make_transform_iterator ( v.end() );
return std::find( boost::make_transform_iterator( v.begin(), lambda ),
transform_end, "c" ) != transform_end;
If your inner lambda is to find "c" in any position I wouldn't use transform iterator here as we want to return a true/false on each inner vector, not just some transformed value, and we would use find_if on the outer-vector and find on the inner one
std::string val = "c";
auto lambda = [ const & ]( std::vector< std::string > const& vInner )
{ return std::find( vInner.begin(), vInner.end(), val ) != v.end(); } ;
return std::find_if( v.begin(), v.end(), lambda );
You can try something similar to the code below
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<std::vector<std::string>> v =
{
{ "1", "a", "aa" },
{ "2", "b", "bb" },
{ "3", "c", "cc" },
{ "4", "d", "dd" }
};
std::vector<std::string>::iterator second;
std::string s = "c";
auto first = std::find_if( v.begin(), v.end(),
[&]( std::vector<std::string> &v1 )
{
return (( second = std::find( v1.begin(), v1.end(), s ) ) != v1.end() );
} );
if ( first != v.end() )
{
size_t i = std::distance( v.begin(), first );
size_t j = std::distance( v[i].begin(), second );
std::cout << v[i][j] << std::endl;
}
return 0;
}
The output is
c
You can do this:
int column = 1; // Set this to the column you need to search;
string target( "c" ); // Set this to the value you need to find
auto found = find_if( myvect.begin(), myvect.end(), [=]( vector< string > row ){ return row[column] == target; } );
cout << ( found == myvect.end() ? "not found" : ( *found2 )[column] ) << endl;
C++11 wont let you define column or target in the capture, if you want to avoid intermediate variables in C++11 though, you can do this, it's just ugly cause of the static_cast. You'd just have to set the "c" and 1 to the target and column:
auto found = find_if( myvect.begin(), myvect.end(), bind( equal_to< string >(), "c", bind( static_cast< const string&( vector<string>::* )( size_t ) const >( &vector< string >::operator[] ), placeholders::_1, 1 ) ) );
I personally would suggest that if your row size is always the same that you put it in a single std::vector like this: vector<string> myvect = { "1", "a", "aa", "2", "b", "bb", "3", "c", "cc", "4", "d", "dd" }; if you do that you can write a template to search for you which will have significantly more flexibility:
template< typename T, int stride >
T* templateFind( const vector< T >& myvect, const T& target, int column )
{
typedef array< T, stride > rowSize;
rowSize* end = ( rowSize* )( &*( myvect.begin() ) ) + ( myvect.size() / stride );
rowSize* result = find_if( ( rowSize* )( &*( myvect.begin() ) ), end, [&]( rowSize row ){ return row[column] == target; } );
return result == end ? nullptr : ( ( T* )result ) + column;
}
And use it like this:
string* found = templateFind< string, 3 >( myvect, "c", 1 );
cout << ( found == nullptr ? "not found" : *found ) << endl;

How to create a new vector with particular columns from existing 2D vector in c++

I have 2D Vector (vector<vector<string>>) with a lot of columns (m*n) (Here I mentioned this 2D Vector as Maintable). I want to create a new vector with a few particular columns from main table.
For Example, Suppose If I have a main table with 12 columns, I want to take any 3 Non Contiguous columns from the main table into new 2D Vector. How to do that?
You can use something as the following
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
//...
const size_t N = 10;
std::string a[] = { "A", "B", "C", "D", "E", "F" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
for ( const std::vector<std::string> &v : v1 )
{
v2.push_back( std::vector<std::string>(std::next( v.begin(), 2 ), std::next( v.begin(), 5 ) ) );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
It is simple to rewrite the code using the C++ 2003 syntax. For example you can write
std::vector<std::vector<std::string>> v1( N,
std::vector<std::string>( a, a + sizeof( a ) / sizeof( *a ) ) );
instead of
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
and so on.
EDIT: If the columns are not adjacent then you can use the following approach
#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <iterator>
#include <algorithm>
int main()
{
const size_t N = 10;
const size_t M = 3;
std::string a[N] = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };
std::vector<std::vector<std::string>> v1( N, std::vector<std::string>( std::begin( a ), std::end( a ) ) );
std::vector<std::vector<std::string>> v2;
v2.reserve( v1.size() );
std::array<std::vector<std::string>::size_type, M> indices = { 2, 5, 6 };
for ( const std::vector<std::string> &v : v1 )
{
std::vector<std::string> tmp( M );
std::transform( indices.begin(), indices.end(), tmp.begin(),
[&]( std::vector<std::string>::size_type i ) { return ( v[i] ); } );
v2.push_back( tmp );
}
for ( const std::vector<std::string> &v : v2 )
{
for ( const std::string &s : v ) std::cout << s << ' ';
std::cout << std::endl;
}
}