特定の条件に基づいて、マップから要素の範囲を消去しようとしていました。 STLアルゴリズムを使用してどうすればよいですか?
最初はremove_if
しかし、remove_ifは連想コンテナでは機能しないため、不可能です。
Mapで機能する「remove_if」同等のアルゴリズムはありますか?
簡単なオプションとして、マップをループして消去することを考えました。しかし、マップをループして安全なオプションを消去していますか?(イテレータは消去後に無効になるため)
私は次の例を使用しました:
bool predicate(const std::pair<int,std::string>& x)
{
return x.first > 2;
}
int main(void)
{
std::map<int, std::string> aMap;
aMap[2] = "two";
aMap[3] = "three";
aMap[4] = "four";
aMap[5] = "five";
aMap[6] = "six";
// does not work, an error
// std::remove_if(aMap.begin(), aMap.end(), predicate);
std::map<int, std::string>::iterator iter = aMap.begin();
std::map<int, std::string>::iterator endIter = aMap.end();
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
return 0;
}
ほぼ。
for(; iter != endIter; ) {
if (Some Condition) {
aMap.erase(iter++);
} else {
++iter;
}
}
元々イテレータをインクリメントしていましたtwiceそこから要素を消去した場合;消去する必要がある要素をスキップする可能性があります。
これは多くの場所で使用され、文書化されているのを見た一般的なアルゴリズムです。
[編集]消去後にイテレータが無効化されるのは正しいですが、消去された要素を参照するイテレータのみが有効で、他のイテレータはまだ有効です。したがって、erase()呼び出しでiter ++を使用します。
私はこのために次のテンプレートを使用します。
namespace stuff {
template< typename ContainerT, typename PredicateT >
void erase_if( ContainerT& items, const PredicateT& predicate ) {
for( auto it = items.begin(); it != items.end(); ) {
if( predicate(*it) ) it = items.erase(it);
else ++it;
}
}
}
これは何も返しませんが、std :: mapからアイテムを削除します。
使用例:
// 'container' could be a std::map
// 'item_type' is what you might store in your container
using stuff::erase_if;
erase_if(container, []( item_type& item ) {
return /* insert appropriate test */;
});
2番目の例(テスト値を渡すことができます):
// 'test_value' is value that you might inject into your predicate.
// 'property' is just used to provide a stand-in test
using stuff::erase_if;
int test_value = 4; // or use whatever appropriate type and value
erase_if(container, [&test_value]( item_type& item ) {
return item.property < test_value; // or whatever appropriate test
});
このドキュメントは excellent SGI STL reference から入手しました。
マップには、新しい要素をマップに挿入しても、既存の要素を指す反復子が無効にならないという重要な特性があります。もちろん、マップから要素を消去しても、イテレータは無効になりません。ただし、もちろん、実際に消去される要素を指すイテレータは除きます。
したがって、消去する要素を指しているイテレータは、もちろん無効になります。このようなことをしてください:
if (some condition)
{
iterator here=iter++;
aMap.erase(here)
}
元のコードには1つの問題しかありません。
for(; iter != endIter; ++iter)
{
if(Some Condition)
{
// is it safe ?
aMap.erase(iter++);
}
}
ここでは、iter
がforループで1回、消去でもう1回インクリメントされ、おそらく無限ループになります。
今、std::experimental::erase_if
はヘッダー<experimental/map>
。
参照: http://en.cppreference.com/w/cpp/experimental/map/erase_if
最初
マップには、新しい要素をマップに挿入しても、既存の要素を指す反復子が無効にならないという重要な特性があります。もちろん、マップから要素を消去しても、イテレータは無効になりません。ただし、もちろん、実際に消去される要素を指すイテレータは除きます。
第二に、次のコードは良いです
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
関数を呼び出すとき、パラメーターはその関数の呼び出しの前に評価されます。
したがって、iter ++がeraseの呼び出しの前に評価されると、イテレーターの++演算子は現在のアイテムを返し、呼び出し後の次のアイテムを指します。
Iron Savior's answer に基づきます。
template< typename ContainerT, class FwdIt, class Pr >
void erase_if(ContainerT& items, FwdIt it, FwdIt Last, Pr Pred) {
for (; it != Last; ) {
if (Pred(*it)) it = items.erase(it);
else ++it;
}
}
ContainerT
アイテムを失い、イテレータから取得する方法があるかどうかを知りたいです。
私見では、remove_if()
に相当するものはありません。
地図を並べ替えることはできません。
したがって、remove_if()
は、erase()
を呼び出すことができる最後に対象のペアを配置できません。
以下のメモから:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
ペア連想コンテナは、可変イテレータの値型が割り当て可能でなければならず、ペアは割り当て可能でないため、可変イテレータを提供できません(簡易イテレータの要件で定義されています)。ただし、ペア連想コンテナは、完全に一定ではないイテレータを提供できます。つまり、式(* i).second = dが有効なイテレータです。
スティーブフォリーの答え 私はより効率的だと感じています。
ここに別の簡単だが効率の低いソリューションがあります:
このソリューションは、remove_copy_if
を使用して、必要な値を新しいコンテナーにコピーし、元のコンテナーの内容を新しいコンテナーの内容と交換します。
std::map<int, std::string> aMap;
...
//Temporary map to hold the unremoved elements
std::map<int, std::string> aTempMap;
//copy unremoved values from aMap to aTempMap
std::remove_copy_if(aMap.begin(), aMap.end(),
inserter(aTempMap, aTempMap.end()),
predicate);
//Swap the contents of aMap and aTempMap
aMap.swap(aTempMap);
キーが2より大きいすべての要素を消去する場合、最良の方法は
map.erase(map.upper_bound(2), map.end());
ただし、範囲に対してのみ機能し、述部に対しては機能しません。
私はこのように使用します
std::map<int, std::string> users;
for(auto it = users.begin(); it <= users.end()) {
if(<condition>){
it = users.erase(it);
} else {
++it;
}
}