diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp index 9683dd619..2afb4fda5 100644 --- a/src/core/common/array.hpp +++ b/src/core/common/array.hpp @@ -477,6 +477,42 @@ public: } } + /** + * This template method removes all elements in the array matching a given indicator. + * + * This method behaves similar to `Remove()`, i.e., a matched element is replaced with the last element in the + * array (using `=` operator on `Type`). So the order of items in the array can change after a call to this method. + * + * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements + * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each + * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: + * + * bool Type::Matches(const Indicator &aIndicator) const + * + * @param[in] aIndicator An indicator to match with elements in the array. + * + */ + template void RemoveAllMatching(const Indicator &aIndicator) + { + for (IndexType index = 0; index < GetLength();) + { + Type &entry = mElements[index]; + + if (entry.Matches(aIndicator)) + { + Remove(entry); + + // When the entry is removed from the array it is + // replaced with the last element. In this case, we do + // not increment `index`. + } + else + { + index++; + } + } + } + /** * This method overloads assignment `=` operator to copy elements from another array into the array. * diff --git a/tests/unit/test_array.cpp b/tests/unit/test_array.cpp index a3fb1d5d9..0840f999c 100644 --- a/tests/unit/test_array.cpp +++ b/tests/unit/test_array.cpp @@ -176,6 +176,14 @@ void TestArrayCopyAndFindMatching(void) { constexpr uint16_t kMaxSize = 10; + enum MatchMode : uint8_t + { + kMatchAll, + kMatchNone, + kMatchOddYear, + kMatchEvenYear, + }; + struct Entry : public Unequatable { Entry(void) = default; @@ -190,12 +198,44 @@ void TestArrayCopyAndFindMatching(void) bool Matches(const char *aName) const { return strcmp(aName, mName) == 0; } bool Matches(uint16_t aYear) const { return aYear == mYear; } + bool Matches(MatchMode aMatchMode) const + { + bool matches = false; + + switch (aMatchMode) + { + case kMatchAll: + matches = true; + break; + case kMatchNone: + matches = false; + break; + case kMatchOddYear: + matches = ((mYear % 2) != 0); + break; + case kMatchEvenYear: + matches = ((mYear % 2) == 0); + break; + } + + return matches; + } + const char *mName; uint16_t mYear; }; + static const MatchMode kMatchModes[] = {kMatchAll, kMatchNone, kMatchEvenYear, kMatchOddYear}; + + static const char *kMatchModeStrings[] = { + "kMatchAll", + "kMatchNone", + "kMatchOddYear", + "kMatchEvenYear", + }; + Entry ps1("PS", 1994); - Entry ps2("PS2", 2000); + Entry ps2("PS2", 1999); Entry ps3("PS3", 2006); Entry ps4("PS4", 2013); Entry ps5("PS5", 2020); @@ -300,6 +340,7 @@ void TestArrayCopyAndFindMatching(void) array2 = array1; array2.RemoveMatching(entryToRemove.mName); + printf("\n- - - - - - - - - - - - - - - - - - - - - - - - "); printf("\nArray after `RemoveMatching()` on entry %s\n", entryToRemove.mName); VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); @@ -315,6 +356,52 @@ void TestArrayCopyAndFindMatching(void) array2.RemoveMatching(entryToRemove.mName); VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + + // Test `RemoveAllMatching()` removing a single matching entry. + + array2 = array1; + array2.RemoveAllMatching(entryToRemove.mName); + + VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + + for (const Entry &entry : array2) + { + VerifyOrQuit(entry != entryToRemove); + VerifyOrQuit(array1.Contains(entry)); + } + + array2.RemoveAllMatching(entryToRemove.mName); + VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + + // Test `RemoveAllMatching()` using different match modes + // removing different subsets. + + for (MatchMode matchMode : kMatchModes) + { + array3 = array2; + + array3.RemoveAllMatching(matchMode); + + printf("\nArray after `RemoveAllMatching(%s)\n", kMatchModeStrings[matchMode]); + + for (const Entry &entry : array3) + { + VerifyOrQuit(!entry.Matches(matchMode)); + VerifyOrQuit(array2.Contains(entry)); + printf("- Name:%-3s Year:%d\n", entry.mName, entry.mYear); + } + + for (const Entry &entry : array2) + { + if (!entry.Matches(matchMode)) + { + VerifyOrQuit(array2.Contains(entry)); + } + } + + array3.RemoveAllMatching(kMatchAll); + VerifyOrQuit(array3.IsEmpty()); + } } printf("\n");