Custom object's vector attribute median c++ - c++

I am trying to find the median value from the third attribute of my objects. My objects are in a vector.
std::vector<Object> obj =
{ Object {V , H , 5},
Object {C , B , 2},
Object {D , F , 6},
Object {B , H , 4},
Object {B , H , 4}
};
answer:
4
I tried to use the nth_element method but it seems that I need to access the attribute.
std::nth_element(obj.begin(), (obj.begin() + obj.end())/2, obj.end());

By default, std::nth_element() applies the operator< to the dereferenced iterators provided as arguments.
In C++, there is no operator< defined for custom classes but the author is free to define (overload) one.
Alternatively, std::nth_element() can be called with a custom less predicate to force a specific order.
Could you show me a generic example ?
Here we go:
an example to get median according to a specific attribute of an object with
partial sorting (aka. std::nth_element()) and
a custom predicate to sort for Object::attr.
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
struct Object {
std::string id1, id2;
int attr;
};
std::ostream& operator<<(std::ostream &out, const Object &obj)
{
return out << "Object { id1: " << obj.id1
<< ", id2: " << obj.id2
<< ", attr: " << obj.attr << " }";
}
// demonstrate
int main()
{
std::vector<Object> obj =
{ Object {"V", "H" , 5},
Object {"C", "B" , 2},
Object {"D", "F" , 6},
Object {"B", "H" , 4},
Object {"B", "H" , 4}
};
// partial sorting with custom predicate (provided as lambda):
std::nth_element(obj.begin(), obj.begin() + obj.size() / 2, obj.end(),
[](const Object &obj1, const Object &obj2) { return obj1.attr < obj2.attr; });
// get result:
const std::vector<Object>::iterator iter
= obj.begin() + obj.size() / 2;
std::cout << "Median: " << *iter << '\n';
}
Output:
Median: Object { id1: B, id2: H, attr: 4 }
Live demo on coliru
Note:
OP mentioned the following snippet:
std::nth_element(obj.begin(), obj.begin() + obj.end()/2, obj.end());
obj.begin() + obj.end()/2 is yet another error.
It had to be obj.begin() + (obj.end() - obj.begin()) / 2.
I used instead the more compact obj.begin() + obj.size() / 2.

Related

C++ Boost RTree overlaps does not work correctly

I am kinda confused and think I get something wrong but I really fail to see it.
I have a boost::geometry::index::rtree that stores 2D boxes of 2D geographic points. Now I try to check if a new box I add to that rtree overlaps with any box already in that rtree. And this check somehow fails for one test and I really don't get why because I do not believe the error is in the rtree/overlaps implementation.
My code is the following (in Visual Studio test environment):
using CoordinateSystem = boost::geometry::cs::geographic<boost::geometry::degree>;
using Point = boost::geometry::model::point<double, 2, CoordinateSystem>;
using RTreeBox = boost::geometry::model::box<Point>;
using RTreeEntry = std::pair<RTreeBox, uint64_t>;
constexpr static auto kRTreeMaxElementsPerNode = 4;
using RTreeAlgorithm = boost::geometry::index::rstar<kRTreeMaxElementsPerNode>;
using RTree = boost::geometry::index::rtree<RTreeEntry, RTreeAlgorithm>;
bool TestAddTreeEntry(RTree& tree, uint64_t index, RTreeBox box)
{
if (!boost::geometry::is_valid(box)) {
boost::geometry::correct(box);
}
std::vector<RTreeEntry> query_results;
tree.query(boost::geometry::index::overlaps(box), std::back_inserter(query_results));
if (query_results.size() > 0)
{
return false;
}
tree.insert(std::make_pair(box, index));
return true;
}
TEST_METHOD(test_rtree_mapping) {
RTree tree;
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 1 }, { 3, 3 })));
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 4, 1 }, { 9, 5 })));
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 4 }, { 2, 9 })));
Assert::IsFalse(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 2.75 }, { 2, 9 })));
Assert::IsFalse(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 4 }, { 3.5, 9 })));
}
The first Assert::IsFalse works - but also unexpectedly only overlaps with the first box ({1, 1}, {3, 3}) and not with the third one ({1, 4}, {2, 9}). The second Assert::IsFalse does not work because the entry is successfully added.
Anyone knows a reason behind this? Has this something to do with geographic coordinates that I do not understand yet?
Kind regards
Overlaps doesn't do what you expect. It's too strict. The docs state:
The function overlaps implements function Overlaps from the OGC Simple Feature Specification.
The OGC document contains:
That's admittedly hard to parse, but that's the nature of technical specifications. Luckily, intersects is much simpler:
a.Intersects(b) ⇔ ! a.Disjoint(b)
DEMO
I created a test program that
allows you to override the RELATION (e.g. bgi::intersects, bgi::overlaps, !bgi::disjoint)
also writes a SVG vizualtion of the shapes involved.
Live On Compiler Explorer
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <fstream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
using CS = bg::cs::geographic<bg::degree>;
using Point = bg::model::point<double, 2, CS>;
using Box = bg::model::box<Point>;
using Tree = std::pair<Box, uint64_t>;
constexpr static auto kRTreeMaxElementsPerNode = 4;
using RTreeAlgorithm = bgi::rstar<kRTreeMaxElementsPerNode>;
using RTree = bgi::rtree<Tree, RTreeAlgorithm>;
#ifndef RELATION
#define RELATION bgi::intersects
#endif
#define RELATION_STR BOOST_PP_STRINGIZE(RELATION)
bool add(RTree& tree, uint64_t index, Box box) {
if (std::string reason; !bg::is_valid(box, reason)) {
std::cerr << "Trying to correct: " << bg::dsv(box) << " (" << reason << ")" << std::endl;
bg::correct(box);
assert(bg::is_valid(box));
}
for (auto it = qbegin(tree, RELATION(box)), e = qend(tree); it != e; ++it)
return false;
tree.insert(std::make_pair(box, index));
return true;
}
int main() {
std::cout << std::boolalpha;
RTree tree;
struct {
Box box;
char const* name;
char const* color;
} const shapes[]{
{{{1.00, 1.00}, {3.00, 3.00}}, "box1", "red"},
{{{4.00, 1.00}, {9.00, 5.00}}, "box2", "green"},
{{{1.00, 4.00}, {2.00, 9.00}}, "box3", "blue"},
{{{1.00, 2.75}, {2.00, 9.00}}, "probe1", "orange"},
{{{1.00, 4.00}, {3.50, 6.00}}, "probe2", "gray"},
};
for (auto const& s : shapes) {
auto idx = (&s - shapes);
auto added = add(tree, idx, s.box);
std::cout << "Adding " << s.name << " as #" << idx << ": "
<< (added ? "ACCEPT" : "REJECTED") << " " << bg::dsv(s.box)
<< "\n";
if (!added) {
for (auto it = qbegin(tree, RELATION(s.box)), e = qend(tree); it != e; ++it) {
std::cout << " - because " << s.name << " " << RELATION_STR
<< " " << shapes[it->second].name << "\n";
}
}
}
{
std::ofstream ofs("output.svg");
bg::svg_mapper<Point> svg(ofs, 400, 400);
auto style = [](std::string c) {
return "fill-rule:nonzero;fill-opacity:0.25;fill:" + c +
";stroke:" + c + ";stroke-width:1;";
};
for (auto const& [b, name, color] : shapes) {
svg.add(b);
}
for (auto const& [b, name, color] : shapes) {
auto h = b.max_corner().get<1>() - b.min_corner().get<1>();
auto mc = b.max_corner();
mc.set<1>(mc.get<1>() - h / 2);
svg.text(mc,
name +
("\n" + boost::lexical_cast<std::string>(bg::dsv(b))),
style(color));
}
for (auto const& [b, _, color] : shapes)
svg.map(b, style(color));
}
}
With either -DRELATION=bgi::intersects or -DRELATION=!bgi::disjoint:
Adding box1 as #0: ACCEPT ((1, 1), (3, 3))
Adding box2 as #1: ACCEPT ((4, 1), (9, 5))
Adding box3 as #2: ACCEPT ((1, 4), (2, 9))
Adding probe1 as #3: REJECTED ((1, 2.75), (2, 9))
- because probe1 bgi::intersects box1
- because probe1 bgi::intersects box3
Adding probe2 as #4: REJECTED ((1, 4), (3.5, 6))
- because probe2 bgi::intersects box3
(replacing bgi::intersects with !bgi::disjoint in the output obviously).
Visualization:
Note how the output changes if you change probe1:
{{{1.00, 2.75}, {1.50, 9.00}}, "probe1", "orange"},
That looks like:
This would behave as you expected even with the bgi::overlaps. Overlaps is just too strict for the expectated output.

std::map: return vectors consisting of keys that have equal values

I have a std::map object. Keys are entity IDs (integers) and values their 2D positions (vectors). The aim is to identify, which entities are in the
same position.
ID Position
1 {2,3}
5 {6,2}
12 {2,3}
54 {4,4}
92 {6,2}
I need to get a vector of vectors consisting of keys, which have equal values.
Output for the example input data above: {1,12}, {5,92}
I know I can copy the 2D positions to vector to vectors and loop the first level vector to find indexes of equal second level vectors. Then work back to find keys by selecting the vectors by index and looping again to find the corresponding keys.
Please suggest a cleaner approach for this.
The point of an std::map is to provide an efficient key to value mapping. What you need is an additional value to key mapping - that can be achieved in multiple ways:
Have with an extra std::map that goes from Position to std::vector<ID>.
Use some sort of spatial partitioning data structure (e.g. quadtree, spatial hash, grid) that makes it efficient to find entities depending on their position.
Use a bidirectional multi-map like boost::bimap. This will allow you to have a bidirectional mapping over collection of values without having to use multiple data structures.
"How do I choose?"
It depends on your priorities. If you want maximum performance, you should try all the approaches (maybe using some sort of templatized wrapper) and profile. If you want elegance/cleanliness, boost::bimap seems to be the most appropriate solution.
You could put your data from the map into a std::mutlimap, with the Position as key and ID as value.
As a side note I wonder if a std::pair might be better than a vector for 2d points.
This answer seems to be best, but I'll offer my code anyway.
Given
#include <iostream>
#include <map>
#include <vector>
// Some definiton of Vector2D
struct Vector2D { int x; int y; };
// and some definition of operator< on Vector2D
bool operator<(Vector2D const & a, Vector2D const & b) noexcept {
if (a.x < b.x) return true;
if (a.x > b.x) return false;
return a.y < b.y;
}
How about:
template <typename M>
auto calculate(M const & inputMap) -> std::vector<std::vector<typename M::key_type> > {
std::map<typename M::mapped_type,
std::vector<typename M::key_type> > resultMap;
for (auto const & vp : inputMap)
resultMap[vp.second].push_back(vp.first);
std::vector<std::vector<typename M::key_type> > result;
for (auto & vp: resultMap)
if (vp.second.size() > 1)
result.emplace_back(std::move(vp.second));
return result;
}
Here's how to test:
int main() {
std::map<int, Vector2D> input{
{1, Vector2D{2,3}},
{5, Vector2D{6,2}},
{13, Vector2D{2,3}},
{54, Vector2D{4,4}},
{92, Vector2D{6,2}}
};
auto const result = calculate(input);
// Ugly print
std::cout << '{';
static auto const maybePrintComma =
[](bool & print) {
if (print) {
std::cout << ", ";
} else {
print = true;
}
};
bool comma = false;
for (auto const & v: result) {
maybePrintComma(comma);
std::cout << '{';
bool comma2 = false;
for (auto const & v2: v) {
maybePrintComma(comma2);
std::cout << v2;
}
std::cout << '}';
}
std::cout << '}' << std::endl;
}
You need to provide a reverse mapping. There are a number of ways to do this, including multimap, but a simple approach if your mapping isn't modified after creation is to iterate over the map and build up the reverse mapping. In the reverse mapping, you map value -> list of keys.
The code below uses std::unordered_map to map std::pair<int, int> (the value in the original map) to std::vector<int> (list of keys in the original map). The building of the reverse map is simple and concise:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
(See the full example for the definition of hash).
There's no need to worry about whether the key exists; it will be created (and the vector of ids will be initialised as an empty vector) when you attempt to access that key using the r[key] notation.
This solution targets simplicity; it's a workable solution if you need to do this and don't care about performance, memory usage or using third-party libraries like Boost.
If you do care about any of those things, or you're modifying the map while doing lookups in both directions, you should probably explore other options.
Live example
#include <iostream>
#include <map>
#include <unordered_map>
#include <vector>
// Define a point type. Use pair<int, int> for simplicity.
using Point = std::pair<int, int>;
// Define a hash function for our point type:
struct hash {
std::size_t operator()(const Point& p) const
{
std::size_t h1 = std::hash<int>{}(p.first);
std::size_t h2 = std::hash<int>{}(p.second);
return h1 ^ (h2 << 1);
}
};
int main() {
// The original forward mapping:
std::map<int, Point> m = {
{1, {2, 3}},
{5, {6, 2}},
{12, {2, 3}},
{54, {4, 4}},
{92, {6, 2}}
};
// Build reverse mapping:
std::unordered_map<Point, std::vector<int>, hash> r;
for (const auto& item : m) {
r[item.second].push_back(item.first);
}
// DEMO: Show all indices for {6, 2}:
Point val1 = {6, 2};
for (const auto& id : r[val1]) {
std::cout << id << " ";
}
std::cout << "\n";
// DEMO: Show all indices for {2, 3}:
Point val2 = {2, 3};
for (const auto& id : r[val2]) {
std::cout << id << " ";
}
std::cout << "\n";
}

C++ Performing mapping between vectors and unique numbers and vice versa

I need to create a dictionary data structure which maps vector to unique numbers and vice versa:
For example for mapping vectors to unique numbers, I can do the following:
vector ---------> unique number
(0,7,8) 0
(0,8,10) 1
(1,2,9) 2
(1,2,10) 3
(1,3,1) 4
Then I need to map back unique numbers to vectors, e.g. given below:
unique numbers --------> vector
0 (0,7,8)
1 (0,8,10)
2 (1,2,9)
3 (1,2,10)
4 (1,3,1)
I tried to do this mapping by creating a structure of integers and vectors -- however, this turned to be very inefficient. Is there some c++ data structure which can perform the forward and reverse mapping efficiently.
boost::bimap has the same functionality as what you're describing.
It is similar to std::map, but allows either element of the pair to be a key to the other element.
There is Boost.Bimap. This is a related question to Bimap Is there a Boost.Bimap alternative in c++11? .
In short, you can have two maps, one for each relationship (id -> vec, vec -> id).
[example as requested in a comment under the question]
Summarily, this makes the array index in the sorted ordering the "unique number" identifying a specific vector<int>, and seeks a specific vector<int> using std::lower_bound. Effectively, get_vec_id is just a convenience function if you find dealing in vector indices (with (size_t)-1 being a "not-found" sentinel value) more intuitive than repeatedly using lower_bound directly and dealing with iterators.
This is not very convenient when you have insert and/or erase of vector<int>s interspersed with lookups, because 1) earlier-retrieved indices are all invalidated, and 2) inserting and erasing in vectors is O(n). But, it's pretty good when the data's prepared first then a multitude of lookups are done.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
template <typename Data>
size_t get_vec_id(const Data& data, const typename Data::value_type& value)
{
auto it = std::lower_bound(data.begin(), data.end(), value);
if (it != data.end() && *it == value)
return it - data.begin();
else
return (size_t)-1;
}
int main()
{
std::vector<std::vector<int>> data =
{ { 0,7,8 },
{ 0,8,10 },
{ 1,2,9 },
{ 1,2,10 },
{ 1,3,1 } };
std::cout << get_vec_id(data, {0, 7}) << '\n';
std::cout << get_vec_id(data, {0, 7, 8}) << '\n';
std::cout << get_vec_id(data, {0, 7, 9}) << '\n';
std::cout << get_vec_id(data, {1, 2, 9}) << '\n';
std::cout << get_vec_id(data, {1, 2, 11}) << '\n';
std::cout << get_vec_id(data, {1, 3, 1}) << '\n';
std::cout << get_vec_id(data, {1, 4, 1}) << '\n';
// to map from a "vec_id" to a vector, use data[vec_id]...
}

C++ remove certain elements of vector

I am new to C++ and want to do vector element elimination.
My vectors are like:
<vector<vector>> objPoints;
<vector<vector>> delPoints;
<vector<vector>> objPoints2;
each objPoints has size 1000x3 and has all points. From objPoints i want to remove delPoints i.e. the (X,Y,Z) values that reside in each row.
Can anyone please tell me syntax?
I interpret your questions as follows: You have two vectors objPoints and delPoints which contain 1000 three-dimensional points. I would encode this as
std::vector<std::array<int,3> > objPoints;
where I assumed you have some raster such that you can desribe your points by int values (otherwise, for double entries, comparison is not that easy).
One benefit of using std::array<int,3> is that you automatically get a lexicographic ordering of the points (that means, specializations of std::less and std::equal_to which can directly be used without the further need to create some).
Algorithm:
First sort your arrays. There might be algorithms where this is not really necessary (see the other answer by #AshwaniDausodia), but the following assumes it. Further, in general by using sorted vectors one can obtain a better performance (at least in the big-O: for unsorted containers, it is roughly O(size1*size2), whereas it is lower for the following algorithm). The sorting first requires an effort of O(size1 log(size1)) + O(size2 log(size2))
Next, traverse both arrays at the same time and each time you find a common element delete it from one of the vectors. As you traverse sorted arrays, where you can always increase only the iterator pointing to the smaller element, this step takes O(size1+size2).
Implementation:
// deletes the elements from container c1 which are also present in c2
// requires sorted containers c1 and c2
//
template< class ContainerType1, class ContainerType2 >
void delete_common_elements(ContainerType1& c1, ContainerType2 const& c2 )
{
for(auto it1=c1.begin(), it2=c2.begin()
; it1!=c1.end() && it2!=c2.end(); )
{
if(*it1==*it2) // eventually change here if your points are not int's
// but are of floating-point type
{
it1 = c1.erase(it1); //it1 is increased here
}
else
{
*it1<*it2 ? ++it1 : ++it2;
}
}
}
DEMO
All together, this requires an effort of O(c1.size()) + O(c1.size() * log(c1.size()) (naturally assuming c1.size()>=c2.size()).
One can easily extend this to take an arbitrary comparison operator instead of the operator==.
If you definately have to use vectors, an easy (but inefficient) way would be to std::find the element in objPoints that you want to remove and then remove it with std::vector::erase.
You may consider read this Q&A on StackOverflow on how to erase elements from STL containers.
The key point is to use the erase-remove idiom to erase items from the vector, and use a lambda to express the erasing condition:
objPoints.erase(
std::remove_if(
objPoints.begin(),
objPoints.end(),
[&delPoints](const Point3D& point)
{
// Erasing condition:
// Is 'point' contained in the 'delPoints' vector?
auto it = std::find(delPoints.begin(), delPoints.end(), point);
return (it != delPoints.end());
}),
objPoints.end());
Full compilable code sample (live here):
#include <algorithm> // for std::find(), std::remove_if()
#include <array> // for std::array
#include <iostream> // for console output
#include <vector> // for std::vector
typedef std::array<int, 3> Point3D;
std::ostream& operator<<(std::ostream& os, const Point3D& point)
{
os << "{" << point[0] << ", "
<< point[1] << ", " << point[2] << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const std::vector<Point3D>& v)
{
if (v.empty())
{
os << "{ <empty> }" << std::endl;
return os;
}
os << "{\n";
for (const auto& point : v)
{
os << " " << point << '\n';
}
os << "}" << std::endl;
return os;
}
int main()
{
std::vector<Point3D> objPoints{{1, 2, 3},
{4, 5, 6},
{11, 22, 33},
{44, 55, 66},
{77, 88, 99}};
std::vector<Point3D> delPoints{{10, 10, 10},
{11, 22, 33},
{44, 55, 66}};
std::cout << "objPoints =\n" << objPoints << std::endl;
std::cout << "delPoints =\n" << delPoints << std::endl;
objPoints.erase(
std::remove_if(
objPoints.begin(),
objPoints.end(),
[&delPoints](const Point3D& point)
{
// Erasing condition:
// Is 'point' contained in the 'delPoints' vector?
auto it = std::find(delPoints.begin(), delPoints.end(), point);
return (it != delPoints.end());
}),
objPoints.end());
std::cout << "\nAfter erasing, objPoints =\n";
std::cout << objPoints << std::endl;
}
Output:
objPoints =
{
{1, 2, 3}
{4, 5, 6}
{11, 22, 33}
{44, 55, 66}
{77, 88, 99}
}
delPoints =
{
{10, 10, 10}
{11, 22, 33}
{44, 55, 66}
}
After erasing, objPoints =
{
{1, 2, 3}
{4, 5, 6}
{77, 88, 99}
}
One obivious method would be:
for each point in delPoints
if point exists in objPoints
delete point from objPoints.
You can do it much more efficiently if you are allowed to sort the vectors. I will provide you with method and pseudo code then you can implement it on you own.
First sort objPoints and delpoints
i=0,j=0
while i < length(objPoints) and j < length(delPoints)
if objPoints[i] > delPoints[j] // Means delPoints[j] is not there in objPoints. If it would have, we would have found it.
j++
else if objPoints[i] < delPoints[j] // Means delPoints[j] is after objPoints[i] if it is there in objPoints
i++
else
erase objPoints[i] // Means we have found delPoints[j], so delete it.
For comaprison, first compare w.r.t x cordinate then y and then z. For sorting, you can use std::sort with same comparison function described in previous line. For deleting, you can use std::vector::erase.
Or you can implement your own fuctions.

best way to get the difference between two containers of type map< classId, set< studentId> >

I have the following two containers
map <classId, set< studentId> > allStudents;
map <classId, set< studentId> > assocStudents;
where assocStudents is a sub-set of all students.
what is the most efficient way to get the difference of allStudents minus assocStudents to get unassocStudents?
map <classId, set< studentId> > unassocStudents;
the best I can think of is to
iterate through each class in allStudents, do a search of that classId in assocStudents.
if the class exists in assocStudents, do a set_difference of the studentId;
otherwise copy the whole studentId set.
is there a smarter way to do this?
EDIT:
I got a bit lost with the answers provided.
assume I have the following data
allStudents contains classId 1, studentId {1, 11, 111, 1111}
classId 2, studentId (2, 22, 222, 2222}
assocStudents contains classId 2, studentId {22, 2222}
I like the final container to be
unassocStudents contains classId 1, studentId {1, 11, 111, 1111}
classId 2, studentId (2, 222}
wouldn't set_difference give me the following
unassocStudents contains classId 1, studentId {1, 11, 111, 1111}
Just use std::set_difference
#include <iterator>
std::set_difference(allStudents.begin(), allStudents.end(),
assocStudents.begin(), assocStudents.end(),
std::inserter(unassocStudents, unassocStudents.end()));
This gives you the answer that you want:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
using classId = int;
using studentId = int;
using map = std::map<classId, std::set<studentId>>;
int main()
{
map allStudents{
{1, {1, 11, 111, 1111}},
{2, {2, 22, 222, 2222}},
};
map assocStudents{
{2, {22, 2222}},
};
map unassocStudents;
// Setup keys.
for (auto const& i : allStudents) {
auto& unassocSet = unassocStudents[i.first];
auto& assocSet = assocStudents[i.first];
std::set_difference(i.second.begin(), i.second.end(),
assocSet.begin(), assocSet.end(),
std::inserter(unassocSet, unassocSet.end()));
}
for (auto const& i : unassocStudents) {
std::cout << "classId = " << i.first << '\n';
for (auto const j : i.second) {
std::cout << "\tstudentId = " << j << '\n';
}
}
}
See it working here.
For those interested.
the following is the C++98 implementation of #Simple's solution
typedef map<int, set <long> > csMap;
for (csMap::const_iterator i = allStudents.begin(); i!=allStudents.end(); ++i){
set<long>& assocSet = assocSet[i->first];
set<long>& unassocSet = unassocSet[i->first];
set_difference (i->second.begin(), i->second.end(),
assocSet.begin(), assocSet.end(),
inserter(unassocSet, unassocSet.end()));
if (unassocSet.empty()) { unassoc.erase( i->first);}
if (assocSet.empty()) { assoc.erase( i->first);}
}
#simple's C++11 solution
// Setup keys.
for (auto const& i : allStudents) {
auto& unassocSet = unassocStudents[i.first];
auto& assocSet = assocStudents[i.first];
std::set_difference(i.second.begin(), i.second.end(),
assocSet.begin(), assocSet.end(),
std::inserter(unassocSet, unassocSet.end()));
}