How to get lower_bound for map in a specific range? - c++

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.

Related

C++ set: find & delete elements based on customized comparator

In my program, set has elements of type pair<char, double>. I also implemented the logic so that set is sorted based on element's 2nd value, from smallest to largest:
using pair_item = std::pair<char, double>;
std::set<pair_item, decltype([](auto e1, auto e2){return e1.second < e2.second;})> pq;
Now, I want to delete an element from set, based on element's 1st value:
auto first_is_w = std::lower_bound(
pq.begin(), pq.end(), [w](const auto& p) {
return p.first == w;
}
);
if (first_is_w != pq.end() && first_is_w->first == w) {
pq.erase(first_is_w);
}
Unfortunately, I got error:
'const A_star(const std::vector<std::tuple<char, char, double> >&, std::unordered_map<char, double>&, char, char)::<lambda(const auto:13&)>' is not derived from 'const std::optional<_Tp>'
{ return *__it < __val; }
~~~~~~^~~~~~~
I'm wondering how should I modify my lambda function to run the search correctly?
Full codes attached below:
#include <iostream>
#include <set>
#include <utility>
using pair_item = std::pair<char, double>;
void printSet(const auto& pq) {
std::cout << "Current pq:" << std::endl;
for (const auto& ele : pq) {
std::cout << "(" << ele.first << ", " << ele.second << "), ";
}
std::cout << std::endl;
}
int main() {
char w = 'A';
std::set<pair_item, decltype([](auto e1, auto e2){return e1.second < e2.second;})> pq;
pq.emplace('A', 30);
pq.emplace('B', 20);
pq.emplace('C', 10);
printSet(pq);
auto first_is_w = std::lower_bound(
pq.begin(), pq.end(), [w](const auto& p) {
return p.first == w;
}
);
if (first_is_w != pq.end() && first_is_w->first == w) {
pq.erase(first_is_w);
}
return 0;
}
Your lambda is fine, but you're using the wrong algorithm. lower_bound requires a sorted range and strict weak ordering comparison, which you do not have for the value you're looking for.
You should use std::find_if, which is an O(N) linear search.
auto first_is_w = std::find_if(
pq.begin(), pq.end(), [w](const auto& p) {
return p.first == w;
}
);
if (first_is_w != pq.end()) {
pq.erase(first_is_w);
}

How to sort array of pair <int, pair <int, int> >, by first element, but descending order?

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;

How to encapsulate custom iterator in function using boost-range

Lately I was using boost-range to create ranges over elements satisfying certain criteria. In all cases I'm using the same kind of filtered range all the time, so that I tried to encapsulate this behaviour in an external function.
This was the point where my problems started. Consider the following example.
#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
int main(int argc, const char* argv[])
{
using namespace boost::adaptors;
std::vector<int> input{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto& element : input | filtered([](auto v) {return v % 2 == 0; } ))
{
std::cout << "Element = " << element << std::endl;
}
std::cout << std::endl;
for (auto& element : myFilter(input,4))
{
std::cout << "Element = " << element << std::endl;
}
return 0;
}
The first for-loop behaves as expected printing 4 and 8. The second for-loop however prints just 4. Why is that?
My second idea was to implement a class having a begin() and end() function. This should be a thin wrapper around a range object.
This was the solution, after fiddling out the type of the range iterator.
struct MyFilter {
MyFilter(const std::vector<int>& c, int r) : c(c), r(r), f([&r](auto v) { return v%r == 0; }) {
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator begin() {
return rng.begin();
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator end() {
return rng.end();
}
std::vector<int> c;
int r;
std::function<bool(int)> f;
boost::range_detail::filtered_range < std::function<bool(int)>, std::vector<int>> rng=c | boost::adaptors::filtered(f);
};
Usage should be something like:
for (auto& element : MyFilter(input, 4)) {
std::cout << "Element = " << element << std::endl;
}
Unfortunately, it prints again just the 4. Whichs is quite strange to me??
Now, I got the solution by myself. I have to remove the "&" in my lambda function to make it work!
In:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
It returns another range adaptor while r captured by reference becomes a dangling reference. To fix it capture r by value:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([r](auto v) { return v%r == 0; });
}; ^
+--- capture by value

Insert with pair vector

Is is possible to use the insert function for a vector but make a pair just like we can do with push_back?
void insert(std::vector<int, std::string>& cont, int value)
{
std::vector<int>::iterator it = std::lower_bound(
cont.begin(),
cont.end(),
value,
std::less<int>()
); // find proper position in descending order
cont.insert((it, std::make_pair(value,""))); // insert before iterator it
}
std::vector<int,std::string> is not allowed, you could change this to std::vector<std::pair<int,std::string>>
furthermore
std::vector<int>::iterator it = std::lower_bound(cont.begin(), cont.end(), value, std::less<int>());
should be changed to compare pairs and return std::vector<std::pair<int,std::string>>::iterator
The function can be written the foollowing way
#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
std::vector<std::pair<int, std::string>>::iterator
insert( std::vector<std::pair<int, std::string>> &v, int value, bool before = true )
{
std::vector<std::pair<int, std::string>>::iterator it;
std::pair<int, std::string> pair( value, "" );
if ( before )
{
it = std::lower_bound( v.begin(), v.end(), pair );
}
else
{
it = std::upper_bound( v.begin(), v.end(), pair );
}
return v.insert( it, pair );
}
int main()
{
std::vector<std::pair<int, std::string>> v { { 1, "A" }, { 2, "B" } };
for ( const auto &p : v )
{
std::cout << p.first << " \"" << p.second << "\"" << std::endl;
}
std::cout << std::endl;
insert( v, 1 );
insert( v, 1, false );
insert( v, 2 );
insert( v, 2, false );
for ( const auto &p : v )
{
std::cout << p.first << " \"" << p.second << "\"" << std::endl;
}
std::cout << std::endl;
return 0;
}
The program output is
1 "A"
2 "B"
1 ""
1 ""
1 "A"
2 ""
2 ""
2 "B"
As for me I would declare the function the following way
std::vector<std::pair<int, std::string>>::iterator
insert( std::vector<std::pair<int, std::string>> &v,
const std::vector<std::pair<int, std::string>>::value_type &value,
bool before = true );

Merge two maps, summing values for same keys in C++

I have two std::map<int,int> maps and wish to merge them into a third map like this:
if the same key is found in both maps, create a pair in the third map with the same key and a value which a sum of values from the first and second map, otherwise just copy a pair to the third map.
I suspect it can be done with std::accumulate, but I don't understand it well enough.
Here is an example how to do the task with using std::accumulate
#include <iostream>
#include <map>
#include <numeric>
int main()
{
std::map<int, int> m1 = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } };
std::map<int, int> m2 = { { 2, 5 }, { 3, 1 }, { 5, 5 } };
for ( const auto &p : m1 )
{
std::cout << "{ " << p.first << ", " << p.second << " } ";
}
std::cout << std::endl;
for ( const auto &p : m2 )
{
std::cout << "{ " << p.first << ", " << p.second << " } ";
}
std::cout << std::endl;
std::map<int, int> m3 = std::accumulate( m1.begin(), m1.end(), std::map<int, int>(),
[]( std::map<int, int> &m, const std::pair<const int, int> &p )
{
return ( m[p.first] +=p.second, m );
} );
m3 = std::accumulate( m2.begin(), m2.end(), m3,
[]( std::map<int, int> &m, const std::pair<const int, int> &p )
{
return ( m[p.first] +=p.second, m );
} );
for ( const auto &p : m3 )
{
std::cout << "{ " << p.first << ", " << p.second << " } ";
}
std::cout << std::endl;
return 0;
}
The output is
{ 1, 1 } { 2, 2 } { 3, 3 } { 4, 4 }
{ 2, 5 } { 3, 1 } { 5, 5 }
{ 1, 1 } { 2, 7 } { 3, 4 } { 4, 4 } { 5, 5 }
In fact only for the second map there is a need to use std::accumulate. The first map can be simply copied or assigned to m3.
For example
std::map<int, int> m3 = m1;
m3 = std::accumulate( m2.begin(), m2.end(), m3,
[]( std::map<int, int> &m, const std::pair<const int, int> &p )
{
return ( m[p.first] +=p.second, m );
} );
An overly generic solution inspired by std::set_union. Unlike the first suggested answer, this should run in O(n) instead of O(n log n).
Edit: it's still O(n log n) because of insertions into the final map.
#include <map>
#include <iostream>
#include <iterator>
#include <algorithm>
template<class InputIterT1, class InputIterT2, class OutputIterT, class Comparator, class Func>
OutputIterT merge_apply(
InputIterT1 first1, InputIterT1 last1,
InputIterT2 first2, InputIterT2 last2,
OutputIterT result, Comparator comp, Func func) {
while (true)
{
if (first1 == last1) return std::copy(first2, last2, result);
if (first2 == last2) return std::copy(first1, last1, result);
if (comp(*first1, *first2) < 0) {
*result = *first1;
++first1;
} else if (comp(*first1, *first2) > 0) {
*result = *first2;
++first2;
} else {
*result = func(*first1, *first2);
++first1;
++first2;
}
++result;
}
}
template<class T>
int compare_first(T a, T b) {
return a.first - b.first;
}
template<class T>
T sum_pairs(T a, T b) {
return std::make_pair(a.first, a.second + b.second);
}
using namespace std;
int main(int argc, char **argv) {
map<int,int> a,b,c;
a[1] = 10;
a[2] = 11;
b[2] = 100;
b[3] = 101;
merge_apply(a.begin(), a.end(), b.begin(), b.end(), inserter(c, c.begin()),
compare_first<pair<int, int> >, sum_pairs<pair<int, int> >);
for (auto item : c)
cout << item.first << " " << item.second << endl;
}
I don't think it will be easy (if not impossible) to find a suitable std::algorithm that serves the purpose.
The easiest way would be to first make a copy of map1 to map_result.
Then iterate through map2 and see if any key already exists in map_result then add the values, else add the key_value pair to map_result.
std::map<int,int> map_result( map1 );
for (auto it=map2.begin(); it!=map2.end(); ++it) {
if ( map_result[it->first] )
map_result[it->first] += it->second;
else
map_result[it->first] = it->second;
}