-2

I've been struggling using map and I'm using struct as key in map the problem is at this part:

void display(const std::map<ChildInfo, unsigned int>& counts, std::vector<ChildInfo>& v) {
    ChildInfo infoUpdate;

    for (auto count = counts.begin(); count != counts.end(); ++count) {
        std::cout << "Value " << count->first << " has count " << count->second << std::endl;
//      counts[infoUpdate.gram] = infoUpdate.gram / count->second;
    }
}

What should I do to divide the chocolate gram by duplicates?

This is my code:

Hmmmmm
  • 79
  • 1
  • 1
  • 6
  • The cleanest way is to: 1) Remove the old entry in the map, 2) Insert a new entry into the map. Map keys are constant, and cannot be modified. And, no, you cannot try to beat the system by declaring they key's class members as `mutable`, and then modify them that way. This will result in undefined behavior. – Sam Varshavchik Sep 24 '16 at 03:20
  • 2
    Additionally, this code is, essentially, using a `double` as a map key. This whole approach is on very, very shaky ground. See [this question for more information](http://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Sam Varshavchik Sep 24 '16 at 03:21
  • The program is flawed, even if you could change the map key. If you use a `double` or determine the key by using any type of floating point calculations, your program is more than likely not going to run with consistent results. If you change compiler or compiler options, expect differing results, even if you use the same input data. This is a bad thing if you're going to compare the results of your program to say, someone else's results (maybe your teacher's?) – PaulMcKenzie Sep 24 '16 at 03:28
  • well, actually I have no idea how to fix this, should I change the key to int? @PaulMcKenzie – Hmmmmm Sep 24 '16 at 03:41

2 Answers2

0

You can't change the key in a map, as that could break the required sort order. If you need to do that, you need to remove the key (and value) from the map, update it, then insert it back in.

Your sort key for ChildInfo isn't right, as if two ChidInfos have the same gram the sort order is indeterminate. You need to add another check to the comparison that will compare something else (the ids) if the first comparison (gram) is equal.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • yes so they each have their id number, so if gram is same, a child with small id should be shown first. but I'm so confused about where to put that comparison.. – Hmmmmm Sep 24 '16 at 03:29
  • @unnnn Put it in the `operator <`. You can write the code almost literally as you just said it... if gram is equal, then compare id (else do what it's currently doing). – TheUndeadFish Sep 24 '16 at 04:06
0

I think you are using the wrong map to solve your problem.

Let's say there are three children that have 1kg chocolate. If I understand you correctly, you want to divide 1kg amongst the three. This leads me to think that key to the map is 1kg and it should map to a set of three children.

std::map<double, std::set<ChildInfo>>

Using a double for key is tricky since the comparison is not as reliable as comparison of integral types. You can use a custom compare function that tests the numbers within a certain tolerance.

Here's a working program:

#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>

using namespace std;

struct ChildInfo
{
   int id;
   double gram;
};

// Custom functor to compare doubles.
struct DoubleCompare
{
   bool operator()(double x1, double x2)
   {
      static double tolerance = 1.0E-10;  // Pick something else if this doesn't work.
      // If the numbers are almost equal.
      return ( fabs(x2-x1) > tolerance && x1 < x2);
   }
};

// Function to compare two ChildInfo to insert them into a set.
bool operator < (const ChildInfo& lhs, const ChildInfo& rhs) { return lhs.id < rhs.id; }

std::ostream& operator<<(std::ostream& str, const ChildInfo& ci) {
   str << " " << ci.id << " gram " << ci.gram << "\n";
   return str;
}

// Read the info for one child.
void input(vector<ChildInfo> &v)
{
   ChildInfo newInfo;

   cin >> newInfo.id >> newInfo.gram;
   v.push_back(newInfo);   
}

// Compute the mapped info from the vector of ChildInfo
void computeMappedInfo(vector<ChildInfo> const& vinfo,
                       std::map<double, std::set<ChildInfo>, DoubleCompare>& mappedInfo)
{
   // Separate them into sets first.
   for ( ChildInfo const& info : vinfo )
   {
      mappedInfo[info.gram].insert(info);
   }

   // Divide the grams in the set.
   for ( auto& item : mappedInfo )
   {
      // The set can't be changed in place.
      // Create a new set and replace the old set with the new set.

      std::set<ChildInfo> newSet;
      size_t s = item.second.size();
      for ( auto info : item.second )
      {
         info.gram /= s;
         newSet.insert(info);
      }
      mappedInfo[item.first] = newSet;
   }
}

// Display the mapped info.
void display(std::map<double, std::set<ChildInfo>, DoubleCompare> const& mappedInfo)
{
   for ( auto const& item : mappedInfo )
   {
      for ( auto& info : item.second )
      {
         std::cout << info << std::endl;
      }
   }
}

int main()
{
   int childNum = 0;
   cin >> childNum;

   std::vector <ChildInfo> info;

   for (int i = 0; i < childNum; ++i) {
      input(info);
   }

   std::map<double, std::set<ChildInfo>, DoubleCompare> mappedInfo;
   computeMappedInfo(info, mappedInfo);
   display(mappedInfo);

   return 0;
}

Input:

8
1 1000
2 1000
3 1000
4 500
5 600
7 800
8 800

Output:

 4 gram 500

 5 gram 600

 7 gram 400

 8 gram 400

 1 gram 333.333

 2 gram 333.333

 3 gram 333.333

See it working at http://ideone.com/Nq5XBH.

R Sahu
  • 204,454
  • 14
  • 159
  • 270