Search code examples
c++sortingmultimapaccumulate

Multimap intelligent sorting by key


I have been working on this problem for sometime now. I have 3 .txt files containing some stuff to read in and arrange within a multimap,

std::multimap<std::string, std::multimap<int, int>>

but if the key already exists, I increment the value and if the key is unique, I put the value in the map.

The 3 .txt files (labelled "x," "y," and "z") contain this:

"x.txt" contains primary keys:

a a a b b b c c c d d d e e e

"y.txt" contains secondary keys:

1 2 2 3 4 4 5 4 4 2 6 6 6 6 6

and "z.txt," which counts the secondary keys, contains:

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

The desired output is this:

1 : 1
2 : 2
3 : 1
4 : 2
5 : 1
4 : 2
2 : 1
6 : 2
6 : 3

Which states that there is one 1 in a, two 2's in a, one 3 in b, two 4's in b... and so on.

My code (below) incorrectly produces this result:

1 : 1
2 : 1
2 : 1
2 : 1
3 : 1
4 : 1
4 : 1
5 : 1
6 : 1
6 : 1
6 : 1

Here is the code: Any suggestions?

//needed to output your result (print map)
template<typename a, typename b>
void print1(const std::multimap<a, b>& thing){
    for (std::multimap<a,b>::const_iterator it = begin(thing); it != thing.end(); it++){
        std::cout << it->first << " : " << it->second << std::endl;
    }
}


std::ifstream X("x.txt");
std::ifstream Y("y.txt");
std::ifstream Z("z.txt");


typedef std::multimap<int, int> Y_Z;
typedef std::multimap<std::string, Y_Z> X_Y_Z;

typedef std::pair<int, int> pair_y_z;
typedef std::pair<std::string, Y_Z> pair_x_y_z;

Y_Z y_z;
X_Y_Z x_y_z;

std::string x_;
int y_;
int z_;


if (X){
    while (X >> x_, Y >> y_, Z >> z_){

        if (x_y_z.empty()) {
            y_z.insert(pair_y_z(y_, z_));
            x_y_z.insert(pair_x_y_z(x_, y_z));
        }
        else {

            X_Y_Z::iterator first_iter = x_y_z.find(x_);
            X_Y_Z::iterator last_iter = x_y_z.upper_bound(x_);

            if (x_y_z.find(x_) == x_y_z.end()) {
                y_z.insert(pair_y_z(y_, z_));
                x_y_z.insert(pair_x_y_z(x_, y_z));
            }
            else{
                for (; first_iter != last_iter; first_iter++){
                    if (x_y_z.find(x_)->second.find(y_) == x_y_z.find(x_)->second.end()){
                        //no match
                        y_z.insert(pair_y_z(y_, z_));
                        x_y_z.insert(pair_x_y_z(x_, y_z));
                        break;
                    }
                    else{
                        //found match
                        x_y_z.find(x_)->second.find(y_)->second += z_;
                        break;
                    }
                }
            }
        }
    }
}

std::cin.get();
print1(y_z);
std::cin.get();

Solution

  • So. It's totally not clear why you are hurting yourself with this complicated data structure. You've got a key with two parts. So let's make the key to your std::map a std::pair<T1, T2>.

    If we do that, the code becomes incredibly simple.

    Source Code:

    #include <iostream>
    #include <fstream>
    #include <utility>
    #include <map>
    
    int main() {
        std::ifstream X("x.txt");
        std::ifstream Y("y.txt");
        std::ifstream Z("z.txt");
    
        std::map<std::pair<std::string, int>, int> data;
    
        std::string x_;
        int y_;
        int z_;
    
        while (X >> x_, Y >> y_, Z >> z_)
            data[std::make_pair(x_, y_)] += z_;
    
        for (auto const & element : data)
            std::cout << std::get<1>(element.first) << " : " << element.second << "\n";
    }
    

    Output:

    1 : 1
    2 : 2
    3 : 1
    4 : 2
    4 : 2
    5 : 1
    2 : 1
    6 : 2
    6 : 3
    

    Further issues:

    There is one discrepancy between my output and the desired output, but I think it's an error in what you expect.

    Your current output expects:

    {a, 1} : 1
    {a, 2} : 2
    {b, 3} : 1
    {b, 4} : 2
    {c, 5} : 1  <--- note that this key is out of order with the next one. 5 > 4.
    {c, 4} : 2
    {d, 2} : 1
    {d, 6} : 2
    {e, 6} : 3
    

    But I'm assuming that the keys should all be sorted properly, which is what my output does.