質問
特定の条件に基づいて、マップから要素の範囲を消去しようとしていました。 STLアルゴリズムを使用してどうすればよいですか?
最初はremove_if
の使用を考えていましたが、remove_ifは連想コンテナでは機能しないため、使用できません。
<!> quot; remove_if <!> quot;はありますか?マップで機能する同等のアルゴリズム?
簡単なオプションとして、マップをループして消去することを考えました。しかし、マップをループして安全なオプションを消去していますか?(イテレータは消去後に無効になるため)
次の例を使用しました:
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;
}
}
元の要素は、要素を消去した場合、反復子を 2回増加させます。消去する必要がある要素をスキップできる可能性があります。
これは、多くの場所で使用され、文書化されているのを見た一般的なアルゴリズムです。
[EDIT]消去後にイテレータは無効になりますが、消去された要素を参照するイテレータのみが有効であり、他のイテレータは引き続き有効です。したがって、erase()呼び出しでiter ++を使用します。
他のヒント
std :: map(およびその他のコンテナー)のerase_if
このために次のテンプレートを使用します。
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
});
優れたSGI STLリファレンスからこのドキュメントを入手しました:
Mapには、次の重要なプロパティがあります マップに新しい要素を挿入する 反復子を無効にしません 既存の要素を指します。消去 地図の要素も のイテレータを無効にします もちろん、実際に されている要素を指す 消去されました。
したがって、消去する要素を指しているイテレータはもちろん無効になります。このようなことをしてください:
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
下のメモから:
http://www.sgi.com/tech/stl/PairAssociativeContainer.html
aペア連想コンテナは、可変イテレータの値型が割り当て可能でなければならず、ペアは割り当て可能でないため、可変トリテレータを提供できません(トリビアルイテレータの要件で定義されています)。ただし、ペア連想コンテナは、完全に一定ではないイテレータを提供できます。つまり、式(* i).second = dが有効なイテレータです。
最初
Mapには、新しい要素をマップに挿入しても、既存の要素を指す反復子が無効にならないという重要なプロパティがあります。もちろん、マップから要素を消去しても、イテレータは無効になりません。ただし、もちろん、実際に消去される要素を指すイテレータは例外です。
第二に、次のコードは良いです
for(; iter != endIter; )
{
if(Some Condition)
{
aMap.erase(iter++);
}
else
{
++iter;
}
}
関数を呼び出すとき、パラメーターはその関数の呼び出しの前に評価されます。
したがって、ererを呼び出す前にiter ++が評価されると、イテレータの++演算子は現在のアイテムを返し、呼び出し後の次のアイテムを指します。
IMHOに相当するremove_if()
はありません。
地図を並べ替えることはできません。
したがって、erase()
は、<=>を呼び出すことができる最後に関心のあるペアを置くことはできません。
Iron Saviorの回答に基づいています。 。
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アイテムを失い、イテレーターから取得する方法があるかどうか興味があります。
Steve Follyの回答の方が効率的だと感じています。
別の簡単だが効率の低いソリューションは次のとおりです。
ソリューションは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;
}
}