Skip to content

Commit 4ec8a32

Browse files
committed
ManyToMany bulk erase
1 parent b03762d commit 4ec8a32

File tree

2 files changed

+123
-54
lines changed

2 files changed

+123
-54
lines changed

BinaryRelations/BinaryRelations.h

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -978,14 +978,85 @@ template <typename LeftType, typename RightType> class ManyToMany
978978
}
979979

980980
/**
981-
@brief Erase multiple pairs from the set.
982-
@param other The set of pairs to erase.
981+
@brief Erase multiple pairs from the set.
982+
This is faster than erasing the pairs one by one.
983+
@param pairs The pairs to erase.
983984
*/
984-
void erase(const ManyToMany<LeftType, RightType>& other) noexcept
985+
void erase(const std::vector<Pair> &pairs) noexcept
985986
{
986-
for (auto pair : other)
987+
if(0 == pairs.size())
988+
return;
989+
990+
auto compare_left_then_right = [](const Pair &a, const Pair &b)
991+
{
992+
if(a.left < b.left) return true;
993+
if(b.left < a.left) return false;
994+
return a.right < b.right;
995+
};
996+
997+
auto compare_right_then_left = [](const Pair &a, const Pair &b)
998+
{
999+
if(a.right < b.right) return true;
1000+
if(b.right < a.right) return false;
1001+
return a.left < b.left;
1002+
};
1003+
1004+
std::vector<Pair> pairs_to_insert = pairs; // Deep copy...
1005+
1006+
// ------------------------
1007+
1008+
std::sort(pairs_to_insert.begin(), pairs_to_insert.end(), compare_left_then_right); // ...so I can sort
1009+
1010+
std::vector<RightType> right_to_insert;
1011+
auto it_end = pairs_to_insert.cend();
1012+
for (auto it = pairs_to_insert.cbegin(); it != it_end; )
1013+
{
1014+
// Collect all right values that have the same left value
1015+
right_to_insert.clear();
1016+
auto left = it->left;
1017+
while(it != it_end && it->left == left)
1018+
{
1019+
right_to_insert.push_back(it->right);
1020+
it++;
1021+
}
1022+
1023+
// Erase them in one go
1024+
auto l2r_it = m_LeftToRight.find(left);
1025+
if (l2r_it != m_LeftToRight.end())
1026+
{
1027+
auto l2r_vec = l2r_it->second;
1028+
auto temp = *l2r_vec;
1029+
m_Count -= l2r_vec->size();
1030+
eraseFromSortedVector(&temp, &right_to_insert, l2r_vec);
1031+
m_Count += l2r_vec->size();
1032+
}
1033+
}
1034+
1035+
// ------------------------
1036+
1037+
std::sort(pairs_to_insert.begin(), pairs_to_insert.end(), compare_right_then_left);
1038+
1039+
std::vector<LeftType> left_to_insert;
1040+
it_end = pairs_to_insert.cend();
1041+
for (auto it = pairs_to_insert.cbegin(); it != it_end; )
9871042
{
988-
erase(pair);
1043+
// Collect all right values that have the same left value
1044+
left_to_insert.clear();
1045+
auto right = it->right;
1046+
while(it != it_end && it->right == right)
1047+
{
1048+
left_to_insert.push_back(it->left);
1049+
it++;
1050+
}
1051+
1052+
// Erase them in one go
1053+
auto r2l_it = m_RightToLeft.find(right);
1054+
if (r2l_it != m_RightToLeft.end())
1055+
{
1056+
auto r2l_vec = r2l_it->second;
1057+
auto temp = *r2l_vec;
1058+
eraseFromSortedVector(&temp, &left_to_insert, r2l_vec);
1059+
}
9891060
}
9901061
}
9911062

@@ -1338,12 +1409,13 @@ template <typename LeftType, typename RightType> class OneToOne
13381409
}
13391410

13401411
/**
1341-
@brief Erase multiple pairs from the set.
1342-
@param other The set of pairs to erase.
1412+
@brief Erase multiple pairs from the set.
1413+
This is equivalent to erasing the pairs one by one.
1414+
@param pairs The pairs to erase.
13431415
*/
1344-
void erase(const OneToOne<LeftType, RightType> &other) noexcept
1416+
void erase(const std::vector<Pair> &pairs) noexcept
13451417
{
1346-
for (auto pair : other)
1418+
for (auto pair : pairs)
13471419
{
13481420
erase( pair);
13491421
}

TestManyToMany.h

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,10 @@ UTEST(TestManyToMany, BulkInsert)
146146
m2m.insert(3, "clementine");
147147

148148
std::vector<ManyToMany<int, std::string>::Pair> vec;
149-
// Add to existing left value
150149
vec.push_back(ManyToMany<int, std::string>::Pair(3, "crabapple"));
151150
vec.push_back(ManyToMany<int, std::string>::Pair(3, "cashew"));
152-
// Completely new left right pairs
153151
vec.push_back(ManyToMany<int, std::string>::Pair(10, "date"));
154152
vec.push_back(ManyToMany<int, std::string>::Pair(10, "dewberry"));
155-
// Existing right values, steal them
156153
vec.push_back(ManyToMany<int, std::string>::Pair(10, "apricot"));
157154
vec.push_back(ManyToMany<int, std::string>::Pair(10, "banana"));
158155
vec.push_back(ManyToMany<int, std::string>::Pair(20, "avocado"));
@@ -182,46 +179,46 @@ UTEST(TestManyToMany, BulkInsert)
182179
ASSERT_TRUE(m2m.contains(20, "apple"));
183180
}
184181

185-
//UTEST(TestManyToMany, BulkErase)
186-
//{
187-
// ManyToMany<int, std::string> mtm;
188-
// mtm.insert(1, "apple");
189-
// mtm.insert(1, "apricot");
190-
// mtm.insert(1, "avocado");
191-
//
192-
// mtm.insert(2, "banana");
193-
// mtm.insert(2, "blueberry");
194-
// mtm.insert(2, "blackberry");
195-
//
196-
// mtm.insert(3, "cherry");
197-
// mtm.insert(3, "coconut");
198-
// mtm.insert(3, "clementine");
199-
//
200-
// std::vector<ManyToMany<int, std::string>::Pair> vec;
201-
// // Erase all of left=1
202-
// vec.push_back(ManyToMany<int, std::string>::Pair(1, "apple"));
203-
// vec.push_back(ManyToMany<int, std::string>::Pair(1, "apricot"));
204-
// vec.push_back(ManyToMany<int, std::string>::Pair(1, "avocado"));
205-
// // Erase some of left=2
206-
// vec.push_back(ManyToMany<int, std::string>::Pair(2, "banana"));
207-
// vec.push_back(ManyToMany<int, std::string>::Pair(2, "blueberry"));
208-
// // This pair is not in the set
209-
// vec.push_back(ManyToMany<int, std::string>::Pair(1000, "zucchini"));
210-
//
211-
// mtm.erase(vec);
212-
//
213-
// ASSERT_EQ(mtm.count(), 4);
214-
// //ASSERT_TRUE(isValid(mtm));
215-
//
216-
// ASSERT_FALSE(mtm.contains(1, "apple"));
217-
// ASSERT_FALSE(mtm.contains(1, "apricot"));
218-
// ASSERT_FALSE(mtm.contains(1, "avocado"));
219-
// ASSERT_FALSE(mtm.contains(2, "banana"));
220-
// ASSERT_FALSE(mtm.contains(2, "blueberry"));
221-
//
222-
// ASSERT_TRUE(mtm.contains(2, "blackberry"));
223-
// ASSERT_TRUE(mtm.contains(3, "cherry"));
224-
// ASSERT_TRUE(mtm.contains(3, "coconut"));
225-
// ASSERT_TRUE(mtm.contains(3, "clementine"));
226-
//}
182+
UTEST(TestManyToMany, BulkErase)
183+
{
184+
ManyToMany<int, std::string> mtm;
185+
mtm.insert(1, "apple");
186+
mtm.insert(1, "apricot");
187+
mtm.insert(1, "avocado");
188+
189+
mtm.insert(2, "banana");
190+
mtm.insert(2, "blueberry");
191+
mtm.insert(2, "blackberry");
192+
193+
mtm.insert(3, "cherry");
194+
mtm.insert(3, "coconut");
195+
mtm.insert(3, "clementine");
196+
197+
std::vector<ManyToMany<int, std::string>::Pair> vec;
198+
// Erase all of left=1
199+
vec.push_back(ManyToMany<int, std::string>::Pair(1, "apple"));
200+
vec.push_back(ManyToMany<int, std::string>::Pair(1, "apricot"));
201+
vec.push_back(ManyToMany<int, std::string>::Pair(1, "avocado"));
202+
// Erase some of left=2
203+
vec.push_back(ManyToMany<int, std::string>::Pair(2, "banana"));
204+
vec.push_back(ManyToMany<int, std::string>::Pair(2, "blueberry"));
205+
// This pair is not in the set
206+
vec.push_back(ManyToMany<int, std::string>::Pair(1000, "zucchini"));
207+
208+
mtm.erase(vec);
209+
210+
ASSERT_EQ(mtm.count(), 4);
211+
//ASSERT_TRUE(isValid(mtm));
212+
213+
ASSERT_FALSE(mtm.contains(1, "apple"));
214+
ASSERT_FALSE(mtm.contains(1, "apricot"));
215+
ASSERT_FALSE(mtm.contains(1, "avocado"));
216+
ASSERT_FALSE(mtm.contains(2, "banana"));
217+
ASSERT_FALSE(mtm.contains(2, "blueberry"));
218+
219+
ASSERT_TRUE(mtm.contains(2, "blackberry"));
220+
ASSERT_TRUE(mtm.contains(3, "cherry"));
221+
ASSERT_TRUE(mtm.contains(3, "coconut"));
222+
ASSERT_TRUE(mtm.contains(3, "clementine"));
223+
}
227224

0 commit comments

Comments
 (0)