有没有一种简单的方法可以重构此代码?

Is there a simple way of refactoring this code?

提问人:tadpole 提问时间:9/26/2022 最后编辑:JeJotadpole 更新时间:10/29/2022 访问量:127

问:

我有一个具有非常相似的重复代码的函数。我喜欢重构它,但不想要任何复杂的映射代码。

该代码基本上过滤掉了表中的列。我通过让比较语句具有简单类型使这个示例变得简单,但实际的比较可能更复杂。

我希望可能有一些模板或 lambda 技术可以做到这一点。

vector<MyRecord*>& MyDb::Find(bool* field1, std::string * field2, int* field3)
{
    std::vector<MyRecord*>::iterator iter;
    filterList_.clear();
    std::copy(list_.begin(), list_.end(), back_inserter(filterList_));

    if (field1)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;
            if (rec->field1 != *field1)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }

    if (field2)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;

            if (rec->field2 != *field2)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }

    if (field3)
    {
        iter = filterList_.begin();
        while (iter != filterList_.end())
        {
            MyRecord* rec = *iter;

            if (rec->field3 != *field3)
            {
                filterList_.erase(iter);
                continue;
            }
            iter++;
        }
    }
    return filterList_;
}

更新:以防万一有人好奇,这是我的最终代码。再次感谢大家。非常容易理解和维护。

vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3)
{
    auto compare = [&](MyRecord* rec) {
        bool add = true;
        if (field1 && rec->field1 != *field1) {
            add = false;
        }
        if (field2 && rec->field2 != *field2) {
            add = false;
        }
        if (field3 && rec->field3 != *field3) {
            add = false;
        }
        return add;
    };

    filterList_.clear();

    std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_), compare);
    return filterList_;
}
C++ 算法 模板 重构 +-标准库 C +20

评论

0赞 Igor Tandetnik 9/26/2022
if ((field1 && rec->field1 != *field1)) || (field2 && rec->field2 != *field2) || (field3 && rec->field3 != *field3) { ...}.预先删除和其他检查,运行一次循环,一次检查所有三个字段。if (field1)
0赞 Iłya Bursov 9/26/2022
只是建议 - 与其创建完整副本然后删除元素,不如将原始列表中的所需元素仅复制到过滤后的列表中
0赞 tadpole 9/26/2022
@IgorTandetnik 就像我说的,我使这个例子变得简单,实际代码比这有更多的列和不同的数据类型。我想避免有一个巨大的 if 语句。无论如何,我可能最终会这样做。
0赞 Sven Nilsson 9/26/2022
如果 templates/lambda 失败,您始终可以使用宏
0赞 tadpole 9/26/2022
我相信是的。所有列都是可选的。

答:

1赞 JeJo 9/26/2022 #1

有没有一种简单的方法可以重构此代码?

据我了解您的算法/意图,使用 std::erase_if) 您可以替换整个 while 循环,如下所示(演示代码):

#include <vector> // std::erase_if

std::vector<MyRecord*> // return by copy as filterList_ is local to function scope
Find(bool* field1 = nullptr, std::string* field2 = nullptr, int* field3 = nullptr)
{
    std::vector<MyRecord*> filterList_{ list_ }; // copy of original
    const auto erased = std::erase_if(filterList_, [=](MyRecord* record) { 
        return record 
            && ((field1 && record->field1 != *field1)
            || (field2 && record->field2 != *field2)
            || (field3 && record->field3 != *field3));
        }
    );
    return filterList_;
}

如果不支持 C++20,也可以使用擦除-删除习惯用语,这实际上发生在 .std::erase_if

评论

0赞 tadpole 9/26/2022
感谢 JeJo 和其他人。我认为你的答案是我所寻找的最相关的答案。我会稍微改变一下。我将使用 find_if 并添加到列表中而不是删除。
0赞 JeJo 9/26/2022
@tadpole 我认为另一个答案是你想要的更多,因为你使用副本。
2赞 apple apple 9/26/2022 #2

你可以使用 std::copy_if (因为你已经/无论如何都会做一个副本)

vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3){
  filterList_.clear();
  std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_),[&](MyRecord* rec){
    // whatever condition you want.
    return field3 && rec->field3 != *field3;
  });
  return filterList_;
}