如何从 std::map 过滤项目?

发布于 2024-07-07 02:05:34 字数 619 浏览 7 评论 0原文

我大致有以下代码。 这可以做得更好或更有效吗? 也许使用 std::remove_if ? 您可以在遍历地图时从地图上删除项目吗? 我们可以避免使用临时地图吗?

typedef std::map<Action, What> Actions;
static Actions _actions;

bool expired(const Actions::value_type &action)
{
  return <something>;
}

void bar(const Actions::value_type &action)
{
  // do some stuff
}

void foo()
{
  // loop the actions finding expired items
  Actions actions;
  BOOST_FOREACH(Actions::value_type &action, _actions)
  {
    if (expired(action))
      bar(action);
    else
      actions[action.first]=action.second;
    }
  }
  actions.swap(_actions);
}

I have roughly the following code. Could this be made nicer or more efficient? Perhaps using std::remove_if? Can you remove items from the map while traversing it? Can we avoid using the temporary map?

typedef std::map<Action, What> Actions;
static Actions _actions;

bool expired(const Actions::value_type &action)
{
  return <something>;
}

void bar(const Actions::value_type &action)
{
  // do some stuff
}

void foo()
{
  // loop the actions finding expired items
  Actions actions;
  BOOST_FOREACH(Actions::value_type &action, _actions)
  {
    if (expired(action))
      bar(action);
    else
      actions[action.first]=action.second;
    }
  }
  actions.swap(_actions);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

假面具 2024-07-14 02:05:34

马克勒索算法的变体,但不需要临时。

for(Actions::iterator it = _actions.begin();it != _actions.end();)
{
    if (expired(*it))
    {
        bar(*it);
        _actions.erase(it++);  // Note the post increment here.
                               // This increments 'it' and returns a copy of
                               // the original 'it' to be used by erase()
    }
    else
    {
        ++it;  // Use Pre-Increment here as it is more effecient
               // Because no copy of it is required.
    }
}

A variation of Mark Ransom algorithm but without the need for a temporary.

for(Actions::iterator it = _actions.begin();it != _actions.end();)
{
    if (expired(*it))
    {
        bar(*it);
        _actions.erase(it++);  // Note the post increment here.
                               // This increments 'it' and returns a copy of
                               // the original 'it' to be used by erase()
    }
    else
    {
        ++it;  // Use Pre-Increment here as it is more effecient
               // Because no copy of it is required.
    }
}
夜还是长夜 2024-07-14 02:05:34

您可以使用擦除(),但我不知道 BOOST_FOREACH 将如何处理无效的迭代器。 map::erase 的文档 指出只有被擦除的迭代器才会被失效了,其他应该没问题。 以下是我如何重构内部循环:

Actions::iterator it = _actions.begin();
while (it != _actions.end())
{
  if (expired(*it))
  {
    bar(*it);
    Actions::iterator toerase = it;
    ++it;
    _actions.erase(toerase);
  }
  else
    ++it;
}

You could use erase(), but I don't know how BOOST_FOREACH will handle the invalidated iterator. The documentation for map::erase states that only the erased iterator will be invalidated, the others should be OK. Here's how I would restructure the inner loop:

Actions::iterator it = _actions.begin();
while (it != _actions.end())
{
  if (expired(*it))
  {
    bar(*it);
    Actions::iterator toerase = it;
    ++it;
    _actions.erase(toerase);
  }
  else
    ++it;
}
清风疏影 2024-07-14 02:05:34

似乎没有人知道的一件事是,当在任何容器上使用时,擦除都会返回一个新的、保证有效的迭代器。

Actions::iterator it = _actions.begin();
while (it != _actions.end())
{
  if (expired(*it))
  {
    bar(*it);
    it = _actions::erase(it);
  }
  else
    ++it;
}

在这种情况下,存储 actions.end() 可能不是一个好的计划,因为我相信迭代器的稳定性无法得到保证。

Something that no one ever seems to know is that erase returns a new, guaranteed-to-be-valid iterator, when used on any container.

Actions::iterator it = _actions.begin();
while (it != _actions.end())
{
  if (expired(*it))
  {
    bar(*it);
    it = _actions::erase(it);
  }
  else
    ++it;
}

Storing actions.end() is probably not a good plan in this case since iterator stability is not guaranteed, I believe.

淤浪 2024-07-14 02:05:34

如果想法是删除过期的项目,为什么不使用 map::erase? 这样,您只需删除不再需要的元素,而不必使用您想要保留的所有元素重建整个副本。

执行此操作的方法是保存指向要擦除的元素的迭代器,然后在迭代结束后将它们全部擦除。

或者,您可以保存访问过的元素,移至下一个元素,然后删除临时元素。 不过,在您的情况下,循环边界会变得混乱,因此您必须自己微调迭代。

根据expired()的实现方式,可能还有其他更好的方法。 例如,如果您正在跟踪时间戳作为映射的键(正如expired()暗示的那样?),您可以对当前时间戳执行upper_bound,并且范围[begin(), upper_bound())中的所有元素都需要进行处理和擦除。

If the idea is to remove expired items, why not use map::erase? This way you only have to remove elements you don't need anymore, not rebuild an entire copy with all the elements that you want to keep.

The way you would do this is to save off the iterators pointing to the elements you want to erase, then erase them all after the iteration is over.

Or, you can save off the element that you visited, move to the next element, and then erase the temporary. The loop bounds get messed up in your case though, so you have to fine tune the iteration yourself.

Depending on how expired() is implemented, there may be other better ways. For example if you are keeping track of a timestamp as the key to the map (as expired() implies?), you can do upper_bound on the current timestamp, and all elements in the range [ begin(), upper_bound() ) need to be processed and erased.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文