From aad14e9f2ffb77c214a12ebea0ced275f977800a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Wed, 15 Jun 2022 16:19:01 -0700 Subject: [PATCH] [array] add `Remove()` and `RemoveMatching()` (#7817) This commit adds `Remove/RemoveMatching()` in `Array` class which remove a given or a matched element from the array. To remove the element, it is replaced by the last element from array, so the order of items in the array can change after call to these method. This commit also updated unit test `test_array` to verify the behavior of newly added methods. --- src/core/common/array.hpp | 47 ++++++++++++++++++++++++++++++++++++ tests/unit/test_array.cpp | 50 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp index ded006f93..9683dd619 100644 --- a/src/core/common/array.hpp +++ b/src/core/common/array.hpp @@ -310,6 +310,27 @@ public: */ IndexType IndexOf(const Type &aElement) const { return static_cast(&aElement - &mElements[0]); } + /** + * This method removes an element from the array. + * + * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined. + * + * To remove @p aElement, it is replaced by the last element in array, so the order of items in the array can + * change after a call to this method. + * + * The method uses assignment `=` operator on `Type` to copy the last element in place of @p aElement. + * + */ + void Remove(Type &aElement) + { + Type *lastElement = PopBack(); + + if (lastElement != &aElement) + { + aElement = *lastElement; + } + } + /** * This method finds the first match of a given entry in the array. * @@ -430,6 +451,32 @@ public: return FindMatching(aIndicator) != nullptr; } + /** + * This template method removes the first element in the array matching a given indicator. + * + * This method behaves similar to `Remove()`, i.e., the matched element (if found) 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 RemoveMatching(const Indicator &aIndicator) + { + Type *entry = FindMatching(aIndicator); + + if (entry != nullptr) + { + Remove(*entry); + } + } + /** * 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 5b1e12783..a3fb1d5d9 100644 --- a/tests/unit/test_array.cpp +++ b/tests/unit/test_array.cpp @@ -35,6 +35,7 @@ #include "common/array.hpp" #include "common/debug.hpp" +#include "common/equatable.hpp" #include "common/instance.hpp" #include "common/type_traits.hpp" @@ -175,7 +176,7 @@ void TestArrayCopyAndFindMatching(void) { constexpr uint16_t kMaxSize = 10; - struct Entry + struct Entry : public Unequatable { Entry(void) = default; @@ -185,7 +186,7 @@ void TestArrayCopyAndFindMatching(void) { } - bool operator==(const Entry &aOther) { return (mName == aOther.mName) && (mYear == aOther.mYear); } + bool operator==(const Entry &aOther) const { return (mName == aOther.mName) && (mYear == aOther.mYear); } bool Matches(const char *aName) const { return strcmp(aName, mName) == 0; } bool Matches(uint16_t aYear) const { return aYear == mYear; } @@ -271,6 +272,51 @@ void TestArrayCopyAndFindMatching(void) VerifyOrQuit(array2.FindMatching(static_cast(2001)) == nullptr); VerifyOrQuit(!array2.ContainsMatching(static_cast(2001))); + // Test removing of entries at every index. + + array1 = array2; + + for (const Entry &entryToRemove : array1) + { + Entry *match; + + // Test `Remove()` + + array2 = array1; + match = array2.Find(entryToRemove); + VerifyOrQuit(match != nullptr); + array2.Remove(*match); + + VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + + for (const Entry &entry : array2) + { + VerifyOrQuit(entry != entryToRemove); + VerifyOrQuit(array1.Contains(entry)); + } + + // Test `RemoveMatching()` + + array2 = array1; + array2.RemoveMatching(entryToRemove.mName); + + printf("\nArray after `RemoveMatching()` on entry %s\n", entryToRemove.mName); + + VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + + for (const Entry &entry : array2) + { + printf("- Name:%-3s Year:%d\n", entry.mName, entry.mYear); + VerifyOrQuit(entry != entryToRemove); + VerifyOrQuit(array1.Contains(entry)); + } + + // Test `RemoveMatchin()` with a non-existing match + + array2.RemoveMatching(entryToRemove.mName); + VerifyOrQuit(array2.GetLength() == array1.GetLength() - 1); + } + printf("\n"); }