From 71de01065bef7c128e740f28d6b160be320b6d8f Mon Sep 17 00:00:00 2001 From: Mat Sutcliffe Date: Tue, 11 Dec 2018 17:12:09 +0000 Subject: [PATCH] Ref T464 Implement removal of one sequence from another in O(n), assuming that elements in both sequences are in the same order in both. --- src/blackgui/views/viewbase.cpp | 5 +---- src/blackmisc/algorithm.h | 21 +++++++++++++++++++ src/blackmisc/sequence.h | 7 +++++++ .../testcontainers/testcontainers.cpp | 18 ++++++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index 21db85c54..07cad5cd3 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -340,10 +340,7 @@ namespace BlackGui else { ContainerType unselectedObjects(container()); - for (const ObjectType &obj : selected) - { - unselectedObjects.remove(obj); - } + unselectedObjects.removeIfInSubset(selected); this->updateContainerMaybeAsync(unselectedObjects); delta = currentRows - unselectedObjects.size(); } diff --git a/src/blackmisc/algorithm.h b/src/blackmisc/algorithm.h index df6a84540..74e712e63 100644 --- a/src/blackmisc/algorithm.h +++ b/src/blackmisc/algorithm.h @@ -22,6 +22,27 @@ namespace BlackMisc { + /*! + * Removes those elements in range 1 that appear also in range 2 leaving only those that + * do not appear in range 2. Returns an iterator one past the new end of range 1. + * \pre All the elements of range 2 must be present in the same order in range 1. + */ + template + auto removeIfIn(I begin1, I end1, J begin2, J end2) + { + auto newEnd = end1; + std::for_each(begin2, end2, [&](const auto &rm) + { + const auto found = std::find(begin1, end1, rm); + Q_ASSERT(found != end1); + if (newEnd == end1) { newEnd = found; } + else { newEnd = std::move(begin1, found, newEnd); } + begin1 = std::next(found); + }); + if (newEnd != end1) { newEnd = std::move(begin1, end1, newEnd); } + return newEnd; + } + namespace Private { //! \private A high quality deterministic pseudo-random number generator. diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 4e2d9bb48..666aea2b1 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -321,6 +321,13 @@ namespace BlackMisc return removeIf([&other](const T &v) { return other.contains(v); }); } + //! Remove all elements if they are in other + //! \pre All elements of other must be present in the same order in this. + void removeIfInSubset(const CSequence &other) + { + erase(BlackMisc::removeIfIn(begin(), end(), other.begin(), other.end()), end()); + } + //! Replace elements matching the given element with a replacement. //! \return The number of elements replaced. int replace(const T &original, const T &replacement) diff --git a/tests/blackmisc/testcontainers/testcontainers.cpp b/tests/blackmisc/testcontainers/testcontainers.cpp index e478d8975..cdd6f8fdc 100644 --- a/tests/blackmisc/testcontainers/testcontainers.cpp +++ b/tests/blackmisc/testcontainers/testcontainers.cpp @@ -65,6 +65,7 @@ namespace BlackMiscTest void joinAndSplit(); void findTests(); void sortTests(); + void removeTests(); void dictionaryBasics(); void timestampList(); void offsetTimestampList(); @@ -199,6 +200,23 @@ namespace BlackMiscTest QVERIFY2(list.sortedBy(&Person::getAge, &Person::getName) == sorted, "sort by multiple members"); } + void CTestContainers::removeTests() + { + const CSequence base { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + const CSequence> subsets + { + {}, { 1 }, { 9 }, { 5 }, { 1, 9 }, { 1, 5 }, { 5, 9 }, { 1, 2 }, + { 8, 9 }, { 4, 5, 6 }, { 1, 5, 9 }, { 3, 7 }, { 3, 5, 7 }, base + }; + for (const auto &subset : subsets) + { + auto copy1 = base, copy2 = base; + copy1.removeIfIn(subset); + copy2.removeIfInSubset(subset); + QVERIFY2(copy1 == copy2, "removeIfInSubset"); + } + } + void CTestContainers::dictionaryBasics() { CTestValueObject key1("Key1", "This is key object 1");