Find an element in vector<vector<string>> - c++

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;

Related

Delete the selected item in forward_list C++

I need to somehow remove an element in a list if it is already in another list. I created a function but it doesn't work. Tell me how to fix it. thank you very much.
void compare(forward_list<string> list_1, forward_list<string> list_2) {
auto prev = list_1.before_begin();
for (int i = 0; i < Size(list_1); i++) {
auto l_front = list_1.begin();
advance(l_front, i);
for (int j = 0; j < Size(list_2); j++) {
auto l_front_2 = list_2.begin();
advance(l_front_2, j);
if (*l_front == *l_front_2)
{
l_front_2 = list_2.erase_after(l_front);
}
}
}
}
It seems you are trying to remove elements from list_2 that are found in list_1. But in this statement
l_front_2 = list_2.erase_after(l_front);
you are using the iterator l_front from list_1 that does not make a sense.
Also if an element in list_2 is removed then due to the expression j++ in the for loop
for (int j = 0; j < Size(list_2); j++) {
the next element in the list will be skipped.
A straightforward approach can look for example the following way as it is shown in the demonstration program below.
#include <iostream>
#include <string>
#include <forward_list>
#include <iterator>
#include <algorithm>
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
for ( auto current = std::begin( list_1 ); current != std::end( list_1 ); )
{
if (std::find( std::begin( list_2 ), std::end( list_2 ), *current ) != std::end( list_2 ))
{
std::string s( *current );
list_1.remove( s );
current = std::begin( list_1 );
}
else
{
std::advance( current, 1 );
}
}
}
int main()
{
std::forward_list<std::string> list_1 = { "A", "B", "A", "C", "D", "B", "E" };
std::forward_list<std::string> list_2 = { "A", "B" };
for (const auto &s : list_1)
{
std::cout << s << ' ';
}
std::cout << '\n';
make_unique( list_1, list_2 );
for (const auto &s : list_1)
{
std::cout << s << ' ';
}
std::cout << '\n';
}
The program output is
A B A C D B E
C D E
A much more simple function definition can be if to use the method remove_if or the general function std::erase_if introduced in the C++ 20 Standard. For example
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
auto found = [&list_2]( const auto &s )
{
return std::find( std::begin( list_2 ), std::end( list_2 ), s ) != std::end( list_2 );
};
list_1.remove_if( found );
}
Or if the compiler supports C++ 20 then
void make_unique( std::forward_list<std::string> &list_1,
const std::forward_list<std::string> &list_2 )
{
auto found = [&list_2]( const auto &s )
{
return std::find( std::begin( list_2 ), std::end( list_2 ), s ) != std::end( list_2 );
};
std::erase_if( list_1, found );
}
The both presented functions remove elements in list_1 that are found in list_2.

How do I get the first layer index of 2D vector?

I need help getting the first layer index of a 2D vector. Each element is unique, so there are no repetitions of element. Here's what I mean bellow.
I have a vector defined as:
vector<vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} }
Then, I want to get the index of where any of the numbers is, on the "first" layer.
By this, I mean if I say
index of 4, it should return 2.
If I say, index of 6, it should return 3.
Thank you in advance!
You can use std::find and std::find_if:
#include <vector>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::vector<int>> vec { {0,1} ,
{2} ,
{3,4},
{5,6} };
// Search for the 4
auto iter = std::find_if(vec.begin(), vec.end(), [](const std::vector<int>& v)
{return std::find(v.begin(), v.end(), 4) != v.end();});
// Output the distance between the item and the beginning of the vector
std::cout << std::distance(vec.begin(), iter);
}
Output:
2
The outer std::find_if searches the std::vector<vector<int>> and the argument to the lambda will be a reference to each inner vector. The inner std::find searches that inner vector for the value.
You could write a function that calculates the index like:
int findIndex(const std::vector<std::vector<int>> &vec, int val)
{
auto it = std::find_if(vec.cbegin(), vec.cend(), [val](const std::vector<int> &v) {
return std::find(v.cbegin(), v.cend(), val) != v.cend();
});
return it != vec.cend() ? std::distance(vec.cbegin(), it) : -1;
}
You can use the standard algorithm std::find_if along with the algorithm std::find.
Here is a demonstrative program.
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::vector<std::vector<int>> v =
{
{ 0, 1 }, { 2 }, { 3, 4 }, { 5, 6 }
};
auto present = []( const auto &v, const auto &value )
{
return std::find( std::begin( v ), std::end( v ), value ) != std::end( v );
};
int value = 4;
size_t i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
value = 6;
i = std::distance( std::begin( v ),
std::find_if( std::begin( v ), std::end( v ),
[&, present, value]( const auto &item )
{
return present( item, value );
} ) );
if ( i != v.size() ) std::cout << value << ": " << i << '\n';
return 0;
}
The program output is
4: 2
6: 3
You can use a hash-table data structure like unordered_map to do this in O(1) time.
unordered_map <int,int> m;
for(int i=0;i<vec.size();i++){
for(int j=0;j<vec[i].size();j++){
m[vec[i][j]] = i;
}
}

Best Way to Find the Array Contains a Given String

I have some arrays of strings and a function that gets a string and return the type of it (the array that the string belongs to)
How can I do it with the best speed?
string arr1[] = {"a", "b", "c"};
string arr2[] = {"d", "e", "f"};
string arr3[] = {"g", "h", "i"};
string arr4[] = {"j", "k", "l"};
...
string getFamily(string input)
{
if(arr1.contains(input)
return "TYPE_1";
...
}
Thanks
This is not elegant, but if you want fast, you can make a prepared unordered_map to do the search which would be useful if this function will be called 100s of times (wasteful if rarely called). Ideally, you can make this container in a class object rather than a global variable and the return type an integral value rather than a string. This would produce a search of O(1) with the cost being in the hashing of the key. But I do not know enough of your requirements.
If you would rather call once, then do as Joachim Pileborg suggested and do a series of std::find calls until you get a hit.
#include <iostream>
#include <unordered_map>
#include <string>
std::unordered_map< std::string, std::string > g_map;
std::string arr1[] = {"a", "b", "c"};
std::string arr2[] = {"d", "e", "f"};
std::string arr3[] = {"g", "h", "i"};
std::string arr4[] = {"j", "k", "l"};
const char * map_value( const std::string & input )
{
std::unordered_map< std::string, std::string >::iterator iter( g_map.find( input ) );
return iter == g_map.end() ? "NOT FOUND" : iter->second.c_str();
}
int main( int argc, char ** argv )
{
// Build the map;
for( int i = 0; i < sizeof( arr1 ) / sizeof( std::string ); ++i )
g_map[arr1[i]] = "TYPE_1";
for( int i = 0; i < sizeof( arr2 ) / sizeof( std::string ); ++i )
g_map[arr2[i]] = "TYPE_2";
for( int i = 0; i < sizeof( arr3 ) / sizeof( std::string ); ++i )
g_map[arr3[i]] = "TYPE_3";
for( int i = 0; i < sizeof( arr4 ) / sizeof( std::string ); ++i )
g_map[arr4[i]] = "TYPE_4";
std::string input;
std::cout << map_value( "b" ) << std::endl;
std::cout << map_value( "z" ) << std::endl;
std::cout << map_value( "eb" ) << std::endl;
std::cout << map_value( "j" ) << std::endl;
std::cout << map_value( "f" ) << std::endl;
return 0;
}
Output:
TYPE_1
NOT FOUND
NOT FOUND
TYPE_4
TYPE_2

Take user input and search through a 2D array

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.

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;
}
}