BLI: simplify removing elements from containers with predicate
Previously removing elements based on a predicate was a bit cumbersome, especially for hash tables. Now there is a new `remove_if` method in some data structures which is similar to `std::erase_if`. We could consider adding `blender::erase_if` in the future to more closely mimic the standard library, but for now this is using the api design of the surrounding code is used.
This commit is contained in:
@@ -886,6 +886,25 @@ class Map {
|
||||
removed_slots_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all key-value-pairs for that the given predicate is true.
|
||||
*
|
||||
* This is similar to std::erase_if.
|
||||
*/
|
||||
template<typename Predicate> void remove_if(Predicate &&predicate)
|
||||
{
|
||||
for (Slot &slot : slots_) {
|
||||
if (slot.is_occupied()) {
|
||||
const Key &key = *slot.key();
|
||||
Value &value = *slot.value();
|
||||
if (predicate(MutableItem{key, value})) {
|
||||
slot.remove();
|
||||
removed_slots_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print common statistics like size and collision count. This is useful for debugging purposes.
|
||||
*/
|
||||
|
@@ -492,6 +492,24 @@ class Set {
|
||||
removed_slots_++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values for which the given predicate is true.
|
||||
*
|
||||
* This is similar to std::erase_if.
|
||||
*/
|
||||
template<typename Predicate> void remove_if(Predicate &&predicate)
|
||||
{
|
||||
for (Slot &slot : slots_) {
|
||||
if (slot.is_occupied()) {
|
||||
const Key &key = *slot.key();
|
||||
if (predicate(key)) {
|
||||
slot.remove();
|
||||
removed_slots_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print common statistics like size and collision count. This is useful for debugging purposes.
|
||||
*/
|
||||
|
@@ -804,6 +804,17 @@ class Vector {
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values for which the given predicate is true.
|
||||
*
|
||||
* This is similar to std::erase_if.
|
||||
*/
|
||||
template<typename Predicate> void remove_if(Predicate &&predicate)
|
||||
{
|
||||
end_ = std::remove_if(this->begin(), this->end(), predicate);
|
||||
UPDATE_VECTOR_SIZE(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a linear search to find the value in the vector.
|
||||
* When found, return the first index, otherwise return -1.
|
||||
|
@@ -348,6 +348,25 @@ class VectorSet {
|
||||
this->remove_contained__impl(key, hash_(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values for which the given predicate is true. This may change the order of elements
|
||||
* in the vector.
|
||||
*
|
||||
* This is similar to std::erase_if.
|
||||
*/
|
||||
template<typename Predicate> void remove_if(Predicate &&predicate)
|
||||
{
|
||||
for (Slot &slot : slots_) {
|
||||
if (slot.is_occupied()) {
|
||||
const int64_t index = slot.index();
|
||||
const Key &key = keys_[index];
|
||||
if (predicate(key)) {
|
||||
this->remove_key_internal(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete and return a key from the set. This will remove the last element in the vector. The
|
||||
* order of the remaining elements in the set is not changed.
|
||||
|
@@ -640,6 +640,24 @@ TEST(map, RemoveDuringIteration)
|
||||
EXPECT_EQ(map.lookup(3), 3);
|
||||
}
|
||||
|
||||
TEST(map, RemoveIf)
|
||||
{
|
||||
Map<int64_t, int64_t> map;
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
map.add(i * i, i);
|
||||
}
|
||||
map.remove_if([](auto item) { return item.key > 100; });
|
||||
EXPECT_EQ(map.size(), 11);
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
if (i <= 10) {
|
||||
EXPECT_EQ(map.lookup(i * i), i);
|
||||
}
|
||||
else {
|
||||
EXPECT_FALSE(map.contains(i * i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(map, LookupKey)
|
||||
{
|
||||
Map<std::string, int> map;
|
||||
|
@@ -576,6 +576,19 @@ TEST(set, RemoveDuringIteration)
|
||||
EXPECT_TRUE(set.contains(3));
|
||||
}
|
||||
|
||||
TEST(set, RemoveIf)
|
||||
{
|
||||
Set<int64_t> set;
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
set.add(i * i);
|
||||
}
|
||||
set.remove_if([](const int64_t key) { return key > 100; });
|
||||
EXPECT_EQ(set.size(), 11);
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
EXPECT_EQ(set.contains(i * i), i <= 10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
|
||||
*/
|
||||
|
@@ -128,6 +128,19 @@ TEST(vector_set, RemoveContained)
|
||||
EXPECT_EQ(set.size(), 0);
|
||||
}
|
||||
|
||||
TEST(vector_set, RemoveIf)
|
||||
{
|
||||
VectorSet<int64_t> set;
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
set.add(i * i);
|
||||
}
|
||||
set.remove_if([](const int64_t key) { return key % 2 == 0; });
|
||||
EXPECT_EQ(set.size(), 50);
|
||||
for (const int64_t i : IndexRange(100)) {
|
||||
EXPECT_EQ(set.contains(i * i), i % 2 == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(vector_set, AddMultipleTimes)
|
||||
{
|
||||
VectorSet<int> set;
|
||||
|
@@ -419,6 +419,15 @@ TEST(vector, Remove)
|
||||
EXPECT_EQ(vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
TEST(vector, RemoveIf)
|
||||
{
|
||||
Vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
vec.remove_if([](const int x) { return x % 2 == 0; });
|
||||
const Vector<int> expected_vec = {1, 3, 5, 7};
|
||||
EXPECT_EQ(vec.size(), expected_vec.size());
|
||||
EXPECT_EQ_ARRAY(vec.data(), expected_vec.data(), size_t(vec.size()));
|
||||
}
|
||||
|
||||
TEST(vector, ExtendSmallVector)
|
||||
{
|
||||
Vector<int> a = {2, 3, 4};
|
||||
|
@@ -45,12 +45,11 @@ void SpreadsheetCache::set_all_unused()
|
||||
void SpreadsheetCache::remove_all_unused()
|
||||
{
|
||||
/* First remove the keys from the map and free the values. */
|
||||
for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) {
|
||||
const Key &key = *it;
|
||||
if (!key.is_used) {
|
||||
cache_map_.remove(it);
|
||||
}
|
||||
}
|
||||
cache_map_.remove_if([&](auto item) {
|
||||
const Key &key = item.key;
|
||||
return !key.is_used;
|
||||
});
|
||||
|
||||
/* Then free the keys. */
|
||||
for (int i = 0; i < keys_.size();) {
|
||||
if (keys_[i]->is_used) {
|
||||
|
Reference in New Issue
Block a user