简体   繁体   中英

Merge two std::sets

I need to merge two sets into a resultant set on basis of one member variable qty if the prices are same. In the below example my resultant set s3 should contain:

Price : 100 Qty : 40

Price : 200 Qty : 60

Please note qty above is a sum of qty in both the sets respective when the price is same.

My question is how do I construct the set s3 below:

Please guide me with the same.

#include <set>
#include <iostream>

using namespace std;

class PriceLevel
{
public:
    int price;
    int qty;

    PriceLevel(int _price, int _qty)
    {
        price = _price;
        qty = _qty;
    }

    friend bool operator<(const PriceLevel &p, const PriceLevel &q);
};

bool operator<(const PriceLevel &p, const PriceLevel &q)
{
    if(p.price < q.price)
    {
        return true;
    }
    else
    {
        return false;
    }
}

int main()
{
    std::set<PriceLevel> s1;
    std::set<PriceLevel> s2;

    PriceLevel p1(100,10);
    PriceLevel p2(200,20);

    PriceLevel p3(100,30);
    PriceLevel p4(200,40);

    s1.insert(p1);
    s1.insert(p2);

    s2.insert(p3);
    s2.insert(p4);

    std::set<PriceLevel> s3;

    set<PriceLevel>::iterator it = s3.begin();

    // How should I Initialize s3

    for(; it != s3.end(); it++)
    {
        cout << "Price: " << it->price << endl;
        cout << "Qty : " << it->qty << endl;
    }
}

If you are absolutely sure that both source sets contain exactly the same prices, you can use the binary version of std::transform .

If they might contain unequal data, you'll have to do it manually, like this:

std::set<PriceLevel> s3;

// How should I Initialize s3
std::set<PriceLevel>::iterator 
    first1 = s1.begin(),
    last1 = s1.end(),
    first2 = s2.begin(),
    last2 = s2.end();
while (first1 != last1 && first2 != last2) {
    if (first1->price < first2->price) {        
        s3.insert(*first1++);
    }
    else if (first1->price > first2->price) {
        s3.insert(*first2++);
    }
    else {
        s3.insert(PriceLevel(first1->price, first1->qty + first2->qty));
        ++first1;
        ++first2;
    }
}
while (first1 != last1) {
     s3.insert(*first1++);
}
while (first2 != last2) {
     s3.insert(*first2++);
}

This is best put in an extra function.

View on IdeOne

If you only need those prices in the result set which existed in both source sets, it is a bit simpler:

while (first1 != last1 && first2 != last2) {
    if (first1->price < first2->price) {        
        ++first1;
    }
    else if (first1->price > first2->price) {
        ++first2;
    }
    else {
        s3.insert(PriceLevel(first1->price, first1->qty + first2->qty));
        ++first1;
        ++first2;
    }
}

set is not an appropriate data structure for your application here. Consider using a map<int, int> instead:

map<int, int> p1, p2, p3; // map price -> quantity

p1[100] = 10;
p1[200] = 20;

p2[100] = 30;
p2[200] = 40;

p3 = p1;
for(auto &i : p2) {
    p3[i.first] += i.second;
}

// Now p3[100]=40 and p3[200]=60.

You can also use a set kind of like a map using set::find :

s3 = s1;
for(auto &i : s2) {
    auto it = s3.find(i);
    if(it == s3.end()) {
        s3.insert(i);
    } else {
        it->qty += i.qty;
    }
}

For this to work, you will have to declare qty as a mutable int , so that it can be modified even if the PriceLevel struct is const (since elements of a set are const ).

If you can't make the variable mutable , then you can try removing the existing set element and then adding a new, merged element.

You are essentially trying to use a set as a map AND merge values with equal keys. You will need to roll your own result (not to mention that it really isn't advisable...). Here is something to get you started.

#include <iostream>
#include <set>

using namespace std;

class PriceLevel
{
public:
    int price;
    int qty;

    PriceLevel() {
        price = 0;
        qty = 0;
    }

    PriceLevel(int _price, int _qty)
    {
        price = _price;
        qty = _qty;
    }

    friend bool operator<(const PriceLevel &p, const PriceLevel &q);

    //Compares two PriceLevel objects and merges their values if their keys are the same.
    //Return value is a std::pair that
    //denotes if the compare was successful and the result is meaningful.        
    static std::pair<bool, PriceLevel> merge_equal(const PriceLevel& p, const PriceLevel& q) {
        std::pair<bool, PriceLevel> result;
        result.first = false;
        if(p.price == q.price) {
            result.first = true;
            result.second.price = p.price;
            result.second.qty = p.qty + q.qty;
        }
        return result;
    }

};

bool operator<(const PriceLevel &p, const PriceLevel &q)
{
    if(p.price < q.price)
    {
        return true;
    }
    else
    {
        return false;
    }
}

int main()
{
    std::set<PriceLevel> s1;
    std::set<PriceLevel> s2;

    PriceLevel p1(100,10);
    PriceLevel p2(200,20);

    PriceLevel p3(100,30);
    PriceLevel p4(200,40);

    s1.insert(p1);
    s1.insert(p2);

    s2.insert(p3);
    s2.insert(p4);

    std::set<PriceLevel> s3;

    //Just in case...the world may explode otherwise.
    if(s1.size() == s2.size()) {

        for(const auto& pl1 : s1) {
            for(const auto& pl2 : s2) {
                //Only insert valid values.
                auto r = PriceLevel::merge_equal(pl1, pl2);
                if(r.first) s3.insert(r.second);
            }
        }

        for(auto it = s3.begin(); it != s3.end(); it++) {
            cout << "Price: " << it->price << endl;
            cout << "Qty : " << it->qty << endl;
        }
    }
}

You can merge two sets with just two lines

#include <set>

template <typename _Ty>
std::set<_Ty> merge(const std::set<_Ty> &x, const std::set<_Ty> &y) const
{
    std::set<_Ty> merged = x; //initial merged set from x
    merged.insert(y.begin(), y.end()); //add contents of y to merged

    return move(merged);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM