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';
}
Related
I want to sort an array containing pair <int, pair <int, int> >, by the first value, in descending order.
I had
pair <int, pair<int, int> > adj[10];
which had values in it, unsorted.
When I used
sort(adj, adj + 10);
it would sort the array based on the adj[i].first value, in ascending order.
However, when I tried sorting in descending order
sort(adj, adj + 10, greater<int>());
It wasn't letting me.
Is there other way to sort by descending order?
You could write:
std::sort(std::begin(adj), std::end(adj),
std::greater<std::pair<int, std::pair<int, int>>>{});
In c++14, you could simplify this to:
std::sort(std::begin(adj), std::end(adj), std::greater<>{});
In c++17, you could simply it a little more:
std::sort(std::begin(adj), std::end(adj), std::greater{});
In c++20, you could simplify this much further:
std::ranges::sort(adj, std::greater{});
You can write a custom predicate using a lambda.
std::sort(std::begin(adj),
std::end(adj),
[](auto const& lhs, auto const& rhs)
{
return lhs.first > rhs.first;
});
Note that the above predicate only considers the first element. You could expand that to look at the further elements if you wanted more complex sorting.
A simple way is to use a lambda expression as for example
#include <utility>
#include <iterator>
#include <algorithm>
//...
std::sort( std::begin( adj ), std::end( adj ),
[]( const auto &a, const auto &b )
{
return b.first < a.first;
} );
Here is a demonstrative program.
#include <iostream>
#include <utility>
#include <functional>
#include <iterator>
#include <algorithm>
int main()
{
const size_t N = 3;
std::pair <int, std::pair<int, int> > adj[N] =
{
{ 1, { 1, 2 } }, { 1, { 2, 1 } }, { 2, { 1, 1 } }
};;
std::sort( std::begin( adj ), std::end( adj ),
[]( const auto &a, const auto &b )
{
return b.first < a.first;
} );
for ( const auto &p : adj )
{
std::cout << " { " << p.first << ", { "
<< p.second.first << ", "
<< p.second.second << " } } ";
}
std::cout << '\n';
return 0;
}
The program output is
{ 2, { 1, 1 } } { 1, { 1, 2 } } { 1, { 2, 1 } }
As you can see from the output if two elements of the array have equal values of the first member of an object of the type std::pair then values of the second member are not taken into account and can be unordered.
Another approach is to use the expression std::gretar<>(). That is to use the default template argument for the functional object std::greater. Here is a demonstrative program.
#include <iostream>
#include <utility>
#include <functional>
#include <iterator>
#include <algorithm>
int main()
{
const size_t N = 3;
std::pair <int, std::pair<int, int> > adj[N] =
{
{ 1, { 1, 2 } }, { 1, { 2, 1 } }, { 2, { 1, 1 } }
};;
std::sort( std::begin( adj ), std::end( adj ), std::greater<>() );
for ( const auto &p : adj )
{
std::cout << " { " << p.first << ", { "
<< p.second.first << ", "
<< p.second.second << " } } ";
}
std::cout << '\n';
return 0;
}
The program output is
{ 2, { 1, 1 } } { 1, { 2, 1 } } { 1, { 1, 2 } }
In this case if two elements have an equal value of the first member then they are ordered according to values of the second member.
If your compiler does not support the C++ 14 Standard then the same effect of using std::greater<> you can achieve using the following lambda expression.
std::sort( std::begin( adj ), std::end( adj ),
[]( const auto &a, const auto &b )
{
return b < a;
} );
since you are using c++14 here is how: sort using std::greater<>{}
std::pair <int, std::pair<int, int> > adj[3];
adj[0]=std::make_pair(10,std::make_pair(0,0));
adj[1]=std::make_pair(1,std::make_pair(0,0));
adj[2]=std::make_pair(7,std::make_pair(0,0));
std::sort(adj, adj+3, std::greater<>{});
std::cout << "Sorted \n";
for (auto x=0; x<3;++x)
std::cout << "x: " << adj[x].first << "\n";
return 0;
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
I want to find the lower_bound for my target in a map(in a range).
I have known another solution:
int main() {
map<int,int> m;
auto it=m.lower_bound(10);
cout<<it->first<<" "<<it->second<<endl;
return 0;
}
BUT, I want to how to use std::lower_bound(m.begin(),m.end(),***).
int main() {
map<int,int> m;
auto it=std::lower_bound(m.begin(),m.end(),10);
cout<<it->first<<" "<<it->second<<endl;
return 0;
}
main.cpp:29:43: required from here
/usr/local/Cellar/gcc/7.3.0_1/include/c++/7.3.0/bits/predefined_ops.h:65:22: error: no match for 'operator<' (operand types are 'std::pair' and 'const int')
{ return *__it < __val; }
The value_type of a map is std::pair<const Key,Value>, so you'll need to supply such a pair as argument.
Given that you are interested only in the key part, it's better to use the overload of std::lower_bound() that accepts a function object:
auto const it = std::lower_bound(m.begin(), m.end(), std::make_pair(10, 0),
[](auto const& a, auto const& b){ return a.first < b.first; });
I believe, from reading the docs, but haven't confirmed, that we can use the map's comparer:
auto const it = std::lower_bound(m.begin(), m.end(), std::make_pair(10, 0),
m.value_comp());
It seems you mean the following
#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>
int main()
{
std::map<int, int> m =
{
{ 2, 1 }, { 4, 2 }, { 6, 3 }, { 8, 4 }, { 10, -1 }, { 10, 0 }, { 12, 2 }
};
int key = 10;
auto it = m.lower_bound( key );
std::cout << "{ " << it->first << ", " << it->second << " }\n";
it = std::lower_bound( std::begin( m ), std::end( m ), key,
[&]( const auto &p, const auto &value ) { return p.first < value; } );
std::cout << "{ " << it->first << ", " << it->second << " }\n";
return 0;
}
The program output is
{ 10, -1 }
{ 10, -1 }
That is in the standard algorithm std::lower_bound you can use a lambda expression.
I need to update all the values (integers) of a map in C++. All the values are going to be multiplied by a float (0.30), so no need to do it one by one.
I can do it manually but I want to know if there is a way to multiply them all at once.
map < string, int >ouremployees = {
{"Haaziq", 80000},
{"Aldo", 100000},
{"Monte", 30000},
{"Carlo", 20000},
{"Afif", 50000},
// THERE ARE MORE WAY WAY MORE BUT ALL THE INT NUMBERS/ VALUES NEED TO BE //MULTIPLIED BY .30 ALL OF THEM
};
In any case you have to do this "manually".:)
Here is a demonstrative program
#include <iostream>
#include <map>
int main()
{
std::map <std::string, int> ouremployees =
{
{ "Haaziq", 80000 },
{ "Aldo", 100000 },
{ "Monte", 30000 },
{ "Carlo", 20000 },
{ "Afif", 50000 },
};
double multiplier = 0.3;
for ( auto &p : ouremployees ) p.second *= multiplier;
for ( const auto &p : ouremployees ) std::cout << p.first << ' ' << p.second << '\n';
return 0;
}
Its output is
Afif 15000
Aldo 30000
Carlo 6000
Haaziq 24000
Monte 9000
Instead of the range-based for loop you could for example use standard algorithm std::for_each.
Fro example
#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>
int main()
{
std::map <std::string, int> ouremployees =
{
{ "Haaziq", 80000 },
{ "Aldo", 100000 },
{ "Monte", 30000 },
{ "Carlo", 20000 },
{ "Afif", 50000 },
};
std::for_each( std::begin( ouremployees ), std::end( ouremployees ),
[multiplier = 0.3]( auto &p ) { p.second *= multiplier; } );
for ( const auto &p : ouremployees ) std::cout << p.first << ' ' << p.second << '\n';
return 0;
}
Pay attention to that maybe the second template argument of the map should be declared as the type argument double.
Thanks a lot indeed!
In the end I ended up doing it like this:
int salary_it; // this var will hold salaries
string name_it;// THIS VARIABLE WILL HOLD THE KEY , EITHER FOOD NAME OR EMPLOYEE NAME
//we change all salaries
map < string, int >::iterator it3b;
for (it3b = ouremployees.begin (); it3b != ouremployees.end (); it3b++)
{
salary_it=it3b->second;
name_it=it3b->first;
cout<<name_it<<" "<<salary_it<<endl;
//NOW WE CAN EITHER USE A find, or [], since the key is being retrived from the
//map itself then there is no risk in using the []
ouremployees[name_it]=salary_it+=salary_it*.30;//instead of .30 we can ask for percentage
}
I need to convert an std::unordered_multimap<Key,T> to an std::vector<std::vector<T>>. I need to do this because my program will need to sort all the data, and maps can't be sorted. An example:
// Map:
{ "A", 1 },
{ "A", 3 },
{ "A", 2 },
{ "B", 5 },
{ "B", 2 },
// Map converted to vector<vector<Value>>:
{ 1, 3, 2 },
{ 5, 2 }
Right now I have this code which works. But I'm wondering if it's the best way to do it.
#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
int main()
{
typedef std::string Key_t;
typedef int Value_t;
typedef std::unordered_multimap<Key_t, Value_t> Map_t;
const Map_t map = {
{ "A", 1 },
{ "A", 3 },
{ "A", 2 },
{ "B", 5 },
{ "B", 2 },
};
std::vector< std::vector< Value_t > > output;
for ( Map_t::const_iterator it = map.cbegin(); it != map.cend(); )
{
std::vector< Value_t > temp;
const Map_t::const_iterator end = map.upper_bound( it->first );
for ( ; it != end; ++it )
temp.push_back( it->second );
output.push_back( temp );
}
// Print the result
for ( const std::vector< Value_t >& values : output )
{
for ( const Value_t& value : values )
std::cout << value << " ";
std::cout << std::endl;
}
}
Output:
1 3 2
5 2
So, now I'm wondering if there's a faster/better way.
here's my attempt.
proof is here: http://goo.gl/JVpHw9
#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main()
{
typedef std::string Key_t;
typedef int Value_t;
typedef std::unordered_multimap<Key_t, Value_t> Map_t;
const Map_t map = {
{ "A", 1 },
{ "A", 3 },
{ "A", 2 },
{ "B", 5 },
{ "B", 2 },
};
std::vector< std::vector< Value_t > > output;
for (auto it = map.begin(); it != map.end(); )
{
auto er = map.equal_range(it->first);
auto tmp = std::vector< Value_t >{};
for( ; it != er.second ; ++it) {
tmp.push_back(it->second);
};
output.push_back(std::move(tmp));
}
// Print the result
for ( const std::vector< Value_t >& values : output )
{
for ( const Value_t& value : values )
std::cout << value << " ";
std::cout << std::endl;
}
}
The usual multimap iteration should work here:
std::vector<std::vector<T>> out;
for (auto it1 = m.begin(), it2 = it1, end = m.end(); it1 != end; it1 = it2)
{
out.emplace_back();
for ( ; it1->first == it2->first; ++it2)
{
out.back().push_back(it2->second);
}
}
Following works for me:
#include <vector>
#include <iostream>
#include <algorithm>
#include <unordered_map>
int main () {
std::unordered_multimap<std::string, int> map;
map = {
{ "A", 1 },
{ "A", 3 },
{ "A", 2 },
{ "B", 5 },
{ "B", 2 },
};
std::vector<int> values;
std::transform(map.begin(), map.end(),
std::back_inserter(values),
[](std::pair<std::string, int> element) {
return element.second;
});
for (auto const& value : values) {
std::cout << value << std::endl;
}
return 0;
}