Merge two maps, summing values for same keys in C++ - 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;
}

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 get lower_bound for map in a specific range?

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.

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

C++ How to create a map from two other maps

How to create map MyMap3 as:
std::map< vector<std::pair<int, int>>, int > MyMap3
Example:
MyMap3[0] = ((1,3) , (1,5), 7 ) // 5 = 4 + 3
MyMap3[1] = ( (2,1) , (2,4), 6 ) // 6 = 1 + 5
Where the key = (vector of pairs) extract from 'MyMap1',
and the value = the sum of the pairs' values in 'MyMap2'.
I also don't care about the pair order, (2,4) is the same as (4,2)
std::map <int, vector<pair<int, int>> > MyMap1;
std::map< std::pair< int, int>, int> MyMap2;
Here is an Example:
MyMap1[0] = (0, (1,3) , (1,5) )
MyMap1[1] = (1, (2,1) , (2,4) )
MyMap2[0] = ( (1,3) , 4 )
MyMap2[1] = ( (1,5) , 3 )
MyMap2[2] = ( (2,1) , 1 )
MyMap2[3] = ( (4,2) , 5 )
I don't know how to do that, and this is my try:
std::map <int, vector<pair<int, int>> > ::iterator it1 = MyMap1.begin();
std::map<std::pair< int, int>, int> ::iterator it2 = MyMap2.begin();
// std::map< vector<pair<int, int> >, int> MyMap3;
std::map< pair<int, int> , int> MyMap3;
int i = 0;
while (it1 != MyMap1.end())
{
vector< pair<int, int> > temp = MyMap1[i];
vector< pair<int, int> > ::iterator it3 = temp.begin();
while (it3 != temp.end())
{
int a = it2->first.first;
int b = it2->first.second;
while (it2 != MyMap2.end())
{
if (it3 == MyMap2.find(std::make_pair(a, b))
MyMap3[std::make_pair(a, b)] += it2->second;
else
++it2;
}
++it3;
++i;
}
++it1;
}
I have syntax error in:
if (it3 == MyMap2.find(make_pair(a, b)))
MyMap3[std::make_pair(a, b)] += it2->second;
Your question is confuse, i decipher you want for each key in map1 to sum the value of each pair in the vector found in the map2 to a single value, and store the result with the map1 key. Not really what your code do…
Example result : http://coliru.stacked-crooked.com/a/a4d77c77fa6896c1
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
using Map1 = std::map< int, std::vector<std::pair<int,int>>>;
using Map2 = std::map< std::pair<int,int>, int >;
using Map3 = std::map< int, int >;
Map3 foo( Map1 const & m1, Map2 const & m2 ) {
auto m2end = m2.end(); // a constant call is move outside the loop
Map3 result;
for( auto & v1 : m1 ) { // each entry in m1 is made of the key as first and the value as second
for( auto & p1 : v1.second ) { // iterate over the vector of pair
auto v2it = m2.find( p1 ); // search for the pair
if ( v2it != m2end ) {
result[v1.first] += v2it->second; // if the pair was found, add the value for it to the result, using the map1 key as key
}
}
}
return result;
}
template<typename T,typename S>
std::ostream& operator<<(std::ostream& os, const std::pair<T,S>& pair )
{
os << "{ " << pair.first << ", " << pair.second << " }";
return os;
}
template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& vec)
{
os << "( ";
for (auto& el : vec)
{
os << el << ", ";
}
os << " )";
return os;
}
template<typename T,typename S>
std::ostream& operator<<(std::ostream& os, const std::map<T,S>& map)
{
for (auto& el : map)
{
os << el.first << " : " << el.second << "\n";
}
return os;
}
int main()
{
Map1 m1 { { 0, { {1,3}, {1,5}} }, { 1, { {2,1}, {2,4}} }, };
Map2 m2 { { {1,3}, 4 }, { {1,5}, 3 }, { {2,1}, 1 }, { {2,4}, 5 }, };
auto m3 = foo( m1, m2);
std::cout << "m1 :\n" << m1 << std::endl;
std::cout << "m2 :\n" << m2 << std::endl;
std::cout << "m3 :\n" << m3 << std::endl;
}