Common character count giving unexpected problems - c++

I have a simple problem where you get two strings where you need to see how many characters are common.
For s1 = "aabcc" and s2 = "dacaa", the output should be
solution(s1, s2) = 3.
I've seen a lot of people solve these types of problems, but I don't want to copy and paste. I want to learn it my way with my solution and improve upon that. I think my solution makes sense but it obviously doesn't, because it doesn't work.
This is my solution:
#include <iostream>
using namespace std;
int main()
{
int counter {};
string s1 = "aabcc";
string s2 = "dacaa";
for (int i {}; i < s1.size(); ++i)
{
for (int j {}; j < s2.size(); ++j)
{
if (s1.at(i) == s2.at(j));
{
cout << "Found character " << s1.at(i) << " at index " << i << " and " << s2.at(j) << " at index " << j << endl;
counter++;
s2.erase(s2.begin()+j);
break;
}
}
cout << s2 << endl;
}
cout << counter << endl;
return 0;
}
This is the output I get (I did a lot of "cout" for debugging purposes.
Found character a at index 0 and d at index 0
acaa
Found character a at index 1 and a at index 0
caa
Found character b at index 2 and c at index 0
aa
Found character a at index 3 and a at index 0
Found character a at index 4 and a at index 0
5
Let's talk about the first iteration. What I don't understand is how s1.at(i) is equal to s2.at(j) at index 0? s1.at(0) is "a" and s2.at(0) is "d". Since when was a = d?
This problem is driving me crazy and I would appreciate if you could explain why my solution acts like this.

There is a typo
if (s1.at(i) == s2.at(j));
Remove the semicolon.
In general the approach is bad because it changes one of the source strings.
I can suggest the following solution by means of using an additional container.
#include <iostream>
#include <string>
#include <string_view>
#include <utility>
#include <map>
#include <iterator>
#include <algorithm>
#include <numeric>
size_t common_letters( std::string_view s1, std::string_view s2 )
{
std::map<char, std::pair<size_t, size_t>> m;
for (char c : s1)
{
if ( m.count( c ) == 0 && s2.find( c ) != std::string_view::npos )
{
m[c].first = std::count( std::begin( s1 ), std::end( s1 ), c );
m[c].second = std::count( std::begin( s2 ), std::end( s2 ), c );
}
}
return std::accumulate( std::begin( m ), std::end( m ), ( size_t )0,
[]( const auto &acc, const auto &item )
{
return acc + std::min( item.second.first, item.second.second );
} );
}
int main()
{
std::string s1 = "aabcc";
std::string s2 = "dacaa";
std::cout << "common_letters( s1, s2 ) = " << common_letters(s1, s2) << '\n';
const char *s3 = "aabcc";
const char *s4 = "dacaa";
std::cout << "common_letters( s3, s4 ) = " << common_letters( s3, s4 ) << '\n';
}
The program output is
common_letters( s1, s2 ) = 3
common_letters( s3, s4 ) = 3
The if statement within the range-based for loop can be rewritten the following way to make the function more efficient
for (char c : s1)
{
if ( auto pos = std::string_view::npos;
m.count( c ) == 0 && ( pos = s2.find( c ) ) != std::string_view::npos )
{
m[c].first = std::count( std::begin( s1 ), std::end( s1 ), c );
m[c].second = std::count( std::next( std::begin( s2 ), pos ), std::end( s2 ), c );
}
}
Or if the compiler supports the C++ 20 then
for ( size_t i = 0; char c : s1)
{
if ( auto pos = std::string_view::npos;
m.count( c ) == 0 && ( pos = s2.find( c ) ) != std::string_view::npos )
{
m[c].first = std::count( std::next( std::begin( s1 ), i ), std::end( s1 ), c );
m[c].second = std::count( std::next( std::begin( s2 ), pos ), std::end( s2 ), c );
}
i++;
}
Or instead of the range-based for loop there can be used an ordinary for loop with iterators. That is the function can look the following way
size_t common_letters( std::string_view s1, std::string_view s2 )
{
std::map<char, std::pair<size_t, size_t>> m;
for ( auto first = std::begin( s1 ), last = std::end( s1 ); first != last; ++first )
{
if ( auto pos = std::string_view::npos;
m.count( *first ) == 0 && ( pos = s2.find( *first ) ) != std::string_view::npos )
{
m[*first] = { std::count( first , last, *first ),
std::count( std::next( std::begin( s2 ), pos ), std::end( s2 ), *first ) };
}
}
return std::accumulate( std::begin( m ), std::end( m ), ( size_t )0,
[]( const auto &acc, const auto &item )
{
return acc + std::min( item.second.first, item.second.second );
} );
}

There is a small typo in your code as Vlad from Moscow said. The reason why that small semicolon ruins the result is that then the code:
if (s1.at(i) == s2.at(j));
{ // From here
cout << "Found character " << s1.at(i) << " at index " << i << " and " << s2.at(j) << " at index " << j << endl;
counter++;
s2.erase(s2.begin()+j);
break;
} // To here
...is not considered as the part of the if statement because a semicolon refers to the end of a statement. So the semicolon ended the if statement, and as the code in the brackets is after the if statement has been ended by the semicolon, it is not considered as a part of the if statement. I hope the explanation is clear :).

Related

Halstead Metrics : Display list of operators and operands from code using C++

I'm really new to C++, I've found this piece of code on GitHub, this code calculate the Halstead Metrics and it's works very well, I'm trying to make it display the operators and operands like this, each operator and operands display with its occurrences :
#include <iostream>
#include <fstream>
#include <regex>
#include <vector>
#include <map>
#include <set>
#include <climits>
#define ll long long
#define pb push_back
#define inf INT_MAX
#define ninf INT_MIN
#define MAXL 100001
#define fo(i,a,b) for(int i=a;i<b;i++)
#define foreach(v, c) for( typeof( (c).begin()) v = (c).begin(); v != (c).end(); ++v)
#define all(a) a.begin(), a.end()
#define in(a,b) ( (b).find(a) != (b).end())
#define pb push_back
#define fill(a,v) memset(a, v, sizeof a)
#define sz(a) ((int)(a.size()))
#define mp make_pair
using namespace std;
const string ops[] = {
";",
",",
// data types
"int",
"float",
"string",
"double",
"long",
"long long",
// control flow
"if",
"else",
"switch",
"case",
"do",
"while",
"for",
// encapsulation
"(",
"{",
"[",
// member access
".",
"->",
// arithmetic
"+",
"-",
"*",
"/",
"%",
"=",
"++",
"--",
// logical
"<",
">",
"<=",
">=",
"==",
// keywords
"break",
"continue",
"class",
"struct",
"default",
"goto",
"operator",
"return"
};
set<string> operators;
map<string, int> operator_counts, operand_counts;
class redundancy_pair
{
public:
string f,s;
int multiplicity;
redundancy_pair( string a, string b, int multiplicity )
{
this->f = a, this->s = b, this->multiplicity = multiplicity;
}
};
// redundancy pairs are necessary to remove matches that match multiple
// patterns for example c++ matches both '++' and '+', multiplicity is
// required to make adjustments to the count variables
vector <redundancy_pair> redundancy_pairs;
void _popualate_redundancy_pairs()
{
for ( set<string>::iterator i = operators.begin(); i != operators.end(); i++)
{
for ( set<string>::iterator j = operators.begin(); j != operators.end(); j++ )
{
if ( (*i) != (*j) )
{
// find num occurences in strings
int num_occur = 0 , pos = 0;
while ( (pos = (*i).find(*j, pos)) != string::npos )
{
num_occur++;
pos += (*j).length();
}
if ( num_occur > 0 )
redundancy_pairs.push_back( redundancy_pair( *j , *i , num_occur ) );
}
}
}
}
void _popualate_operators ()
{
int size = *(&ops + 1) - ops;
for ( int i=0 ; i < size; i++ )
operators.insert( ops[i] );
}
void _adjust_redundancy()
{
for ( vector<redundancy_pair>::iterator it = redundancy_pairs.begin(); it != redundancy_pairs.end(); it++ )
{
// second exists
if ( operator_counts.find( (*it).s ) != operator_counts.end())
operator_counts[(*it).f] = operator_counts[(*it).f] - operator_counts[(*it).s]*((*it).multiplicity);
}
return;
}
int main()
{
// fill the operators
_popualate_operators();
_popualate_redundancy_pairs();
// we now create a regex for identifier
regex identifier_def( "[A-Za-z][A-Za-z0-9]*" );
// numbers defined at word boundary , important condition
// for matching numbers is that they have to be separated by
// word boundaries
regex number_def( "\\b([0-9]+)\\b" );
smatch sm;
ifstream file( "code.txt" );
string input;
if ( file.is_open() )
{
while ( getline( file, input ) )
{
// now check for operators in the line
for ( set<string>::iterator op = operators.begin(); op != operators.end(); op++)
{
int pos = 0;
// for every operator scan the entire line for multiple matches
while (( pos = input.find( *op, pos )) != string::npos)
{
// found an operator
if ( operator_counts.find( *op ) == operator_counts.end() )
operator_counts.insert({ *op, 1 });
else
operator_counts[*op]++;
pos += (*op).length();
}
}
// now lets check for identifiers
string::const_iterator pos( input.cbegin() );
while( regex_search (pos, input.cend(), sm , identifier_def))
{
// check if identifier is an operator
if ( operators.find( sm[0] ) != operators.end() )
{
pos += sm.position() + sm.length();
continue;
}
string operand = sm[0];
// if not add to map
if ( operand_counts.find( operand ) != operand_counts.end() )
operand_counts[operand]++;
else
operand_counts.insert( make_pair( operand, 1 ) );
pos += sm.position() + sm.length();
}
// search for numbers
pos = input.cbegin();
while ( regex_search( pos, input.cend(), sm, number_def ) )
{
// check if identifier is an operator
if ( operators.find( sm[0] ) != operators.end() )
{
pos += sm.position() + sm.length();
continue;
}
string operand = sm[0];
// cout << "Operand : " << operand << endl;
// if not add to map
if ( operand_counts.find( operand ) != operand_counts.end() )
operand_counts[operand]++;
else
operand_counts.insert( make_pair( operand, 1 ) );
pos += sm.position() + sm.length();
}
}
_adjust_redundancy();
}
int N1=0,n1=0,n2=0,N2=0;
for ( map<string, int>::iterator it=operator_counts.begin(); it != operator_counts.end(); it++ )
{
if ( (*it).second ) n1++;
N1 += (*it).second;
}
for ( map<string, int>::iterator it=operand_counts.begin(); it != operand_counts.end(); it++ )
{
if( (*it).second ) n2++;
N2 += (*it).second;
}
printf("n1:%d, n2:%d, N1:%d, N2:%d\n", n1, n2 , N1, N2);
// compute the halstead metrics now
// program size defined as the sum of
// all operands and operators
int size = N1 + N2;
// Vocabulary size -- Size of the vocabulary
// defined as sum of distinct operands and operators
int vocab_size = n1+n2;
// Volume - Program Volume , defined as follows:
// Volume = size x log ( vocab_size )
double volume = size*log2(vocab_size);
// Difficulty = ( n1/2 ) x ( N2/n2 ) and level = 1/difficulty
double difficulty = (double(n1)/2) * (double(N2)/double(n2));
double level = (1/difficulty);
// effort = volume x difficulty
double effort = volume*difficulty;
cout << "Size : " << size << endl;
cout << "Vocabulary Size : " << vocab_size << endl;
cout << "volume : " << volume << endl;
cout << "difficulty : " << difficulty << endl;
cout << "level : " << level << endl;
cout << "effort : " << effort << endl;
}
For a beginner it's really hard to modify this code, I just want to know which part, I need to look in order to display operators and operands

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.

Printing all substrings between/after occurrence of a character in C++

I am trying to grab all substrings in between or after the occurrences of a certain character.
Specifically with for search query urls (grabbing the options), for example if I have:
std::string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8"
I need to output the strings in between and after (for the last occurrence) the "&" character
so the output would be:
rlz=1C1CHBF_enUS851US851
oq=i+need+help
aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7
sourceid=chrome
ie=UTF-8
I understand how to do this with one string, but I am stuck trying to implement it into a loop. This has to be done with several urls of different lengths and number of options.
So far I can only grab one substring, between the first and second occurrences of character, but I need to grab all of them in any given url.
int a = url.find("&") + 1;
int b = url.find("&", url.find("&") + 1);
int c = (b - a);
std::string option = url.substr(a, c);
Just find the next & from the previous one in a loop and exit the loop if you cannot find any & more and take care of the first element:
vector<string> foo(const string& url)
{
vector<string> result;
auto a = url.find("?");
if (a == string::npos) return result;
auto b = url.find("&");
if (b == string::npos)
{
result.push_back(url.substr(a + 1, string::npos));
return result;
}
result.push_back(url.substr(a + 1, b - a - 1));
do
{
a = b;
b = url.find("&", a + 1);
result.push_back(url.substr(a + 1, b - a - 1));
} while (b != string::npos);
return result;
}
works for your example: https://ideone.com/SiRZQB
Tbh I genuinely think that you should use a proper URI parser for this job as there could be a lot of fringe cases. But here you go:
#include <iostream>
#include <string>
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8";
char delimiter = '&';
size_t start = url.find(delimiter);
size_t end;
while (start != std::string::npos) {
end = url.find(delimiter, start + 1);
std::cout << url.substr(start + 1, end - start - 1) << std::endl;
start = end;
}
}
Playground: http://cpp.sh/8pshy7
You can try the following code which uses the regular expressions to parse the url.
#include <regex>
#include <iostream>
#include <string>
using namespace std;
int main(){
string url = "https://www.google.com/search?q=i+need+help&rlz=1C1CHBF_enUS851US851&oq=i+need+help&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7&sourceid=chrome&ie=UTF-8";
regex rg("[\?&](([^&]+)=([^&]+))");
for(smatch sm; regex_search(url, sm, rg); url=sm.suffix())
cout << sm[1] <<endl;
return 0;
}
You can use just an ordinary for loop as for example
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help"
"&rlz=1C1CHBF_enUS851US851"
"&oq=i+need+help"
"&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7"
"&sourceid=chrome&ie=UTF-8";
char c = '&';
size_t n = std::count_if( std::begin( url ), std::end( url ),
[=]( const auto &item )
{
return item == c;
} );
std::vector<std::string> v;
v.reserve( n );
for ( auto pos = url.find( c, 0 ); pos != std::string::npos; )
{
auto next = url.find( c, ++pos );
auto n = ( next == std::string::npos ? url.size() : next ) - pos;
v.push_back( url.substr( pos, n ) );
pos = next;
}
for ( const auto &s : v ) std::cout << s << '\n';
}
The program output is
rlz=1C1CHBF_enUS851US851
oq=i+need+help
aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7
sourceid=chrome
ie=UTF-8
Or you can write a separate function as for example
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
std::vector<std::string> split_url( const std::string &url, char c = '&' )
{
size_t n = std::count_if( std::begin( url ), std::end( url ),
[=]( const auto &item )
{
return item == c;
} );
std::vector<std::string> v;
v.reserve( n );
for ( auto pos = url.find( c, 0 ); pos != std::string::npos; )
{
auto next = url.find( c, ++pos );
auto n = ( next == std::string::npos ? url.size() : next ) - pos;
v.push_back( url.substr( pos, n ) );
pos = next;
}
return v;
}
int main()
{
std::string url = "https://www.google.com/search?q=i+need+help"
"&rlz=1C1CHBF_enUS851US851"
"&oq=i+need+help"
"&aqs=chrome.0.69i59j0l3j69i60l2.4646j0j7"
"&sourceid=chrome&ie=UTF-8";
auto v = split_url(url );
for ( const auto &s : v ) std::cout << s << '\n';
}

Recursive Function that returns all substrings of a string

I need to implement a function in C++,
vector<string> generateSubstrings(string s),
that returns a vector of all substrings of a string. For example, the substrings of the string “rum” are the seven strings
“r”, “ru”, “rum”, “u”, “um”, “m”, “”.
The function has to be recursive and has to return the results as a vector.
Here is my code so far. It's only printing "r", "ru" and "rm". I'm having alot of trouble implementing this function. I've been working on this for the past few hours but I just can't figure out how to get it working as stated, so any help would be appreciated.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
vector<string> generateSubstrings(string s, int num){
int index = num;
int SIZE = s.size();
vector<string> substrings;
if(index == s.size()){//BASE CASE
string temp = s.substr(index,1);
substrings.push_back(temp);
}
else{
for(int i = 0; i < SIZE; ++i){
string temp = s.at(index) + s.substr(i,i);
substrings.push_back(temp);
}
generateSubstrings(s, num + 1);
}
return substrings;
}
int main() {
vector<string> vec(20);
vec = generateSubstrings("rum", 0);
cout << endl << endl;cout << "PRINTING VECTOR" << endl;
for ( int i = 0; i<vec.size();++i){
cout << vec.at(i);
cout << endl;
}
cout << "DONE";
}
In your assignment there is written that the recursive function has to be declared like
vector<string> generateSubstrings(string s),
But you are trying to make another function recursive that declared like
vector<string> generateSubstrings(string s, int num);
So in any case your solution does not satisfy the requirement of the assignment.
The function can look the following way
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return {};
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
for ( const std::string &t : generateSubstrings( s.substr( 1 ) ) )
{
v.push_back( t );
}
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
Its output is
r
ru
rum
u
um
m
If you need also to include an empty string then you should change condition
if ( s.empty() ) return {};
in appropriate way. For example
if ( s.empty() ) return { "" };
Also in this case you should write
v.reserve( s.size() * ( s.size() + 1 ) / 2 + 1 );
Also you can replace the loop in the shown function with method insert. For example
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return {};
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
std::vector<std::string> v2 = generateSubstrings( s.substr( 1 ) );
v.insert( v.end(), v2.begin(), v2.end() );
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
The program output will be the same as shown above.
Here is a program modification that includes an empty string in the vector.
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> generateSubstrings( std::string s )
{
if ( s.empty() ) return { "" };
std::vector<std::string> v;
v.reserve( s.size() * ( s.size() + 1 ) / 2 + 1 );
for ( std::string::size_type i = 0; i < s.size(); i++ )
{
v.push_back( s.substr( 0, i + 1 ) );
}
std::vector<std::string> v2 = generateSubstrings( s.substr( 1 ) );
v.insert( v.end(), v2.begin(), v2.end() );
return v;
}
int main()
{
std::string s( "rum" );
for ( const std::string &t : generateSubstrings( s ) )
{
std::cout << t << std::endl;
}
return 0;
}
Here's an answer using Python. It prints the correct result for "rum", but for "rumm" it prints two "m" substrings for obvious reasons:
def substrings(s):
result = []
if len(s) == 0:
result.append("")
if len(s) > 0:
result += substrings(s[1:])
for n in range(1,len(s)+1):
result.append(s[0:n])
return result
print substrings("rum")
print substrings("rumm")
The idea of the algorithm is the following: for "rum", the substrings are the substrings of "um" followed by "r", "ru" and "rum". For "um", the substrings are the substrings of "m" followed by "u" and "um". For "m", the substrings are the substrings of "" followed by "m". For "", the substrings are simply "". So, the final list is "", "m", "u", "um", "r", "ru", "rum".
Although this isn't C++, you should be able to translate the code to C++. But that may not necessarily be what you want as "rumm" has two "m" substrings. If you think that "rumm" should have only one "m" substring, please leave a comment and I'll post another answer.
First, you should pay attention about code indent.
Then, I don't look your code, I wrote some code to achieve your aim, as follow:
void generateSubstrings(string s, int num, vector<string> &sta)
{
if (num == s.size())
return;
auto b = begin(s) + num;
string temp = "";
temp += *b;
sta.push_back(temp);
b++;
while (b != end(s))
{
temp += *b;
sta.push_back(temp);
b++;
}
generateSubstrings(s, num + 1, sta);
}

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;