Newsgroup: it.comp.lang.c++
Titolo: Rimuovere da mappa, per valore

Salve a tutti,
voglio scorrere una mappa, eliminando tutte le entry che hanno un
certo valore "target".
Mi pare di capire che chiamare erase su un iteratore invalida solo
quell'iteratore e nessun altro, e' corretto? In questo caso, sarebbe
corretta una cosa cosi'?
map<...>::iterator itr = myMap.begin();
map<...>::iterator end = myMap.end();
while(itr != end) {
	map<...>::iterator tmp = itr;
	++itr;
	if(tmp->second == target)
		myMap.erase(tmp);
}
Thx,
Pleg


Purtroppo le map non sono il massimo per effettuare rimozioni di
elementi multipli senza conoscere le chiavi corrispondenti. Ti
conviene probabilmente fare una prima passata immagazzinando le chiavi
degli elementi che ti interessano in un vector o in una list, e poi
effettuare la cancellazione per chiave.
Forse funziona come hai scritto tu, ma non credo sia sicuro al 100%.
Il motivo è che comunque gli iteratori di una map sono bidirezionali,
e magari con qualche implementazione dello standard particolare
qualcosa va male perché comunque la direzione "all'indietro" di
quell'iteratore non va più bene.
Avevo anche provato, sbagliando, a scrivere questo (lo riporto solo a
livello didattico, giusto perché l'ho scritto prima di accorgermi
dell'errore):
// ---------------------------------------------------------------
#include <algorithm>
#include <functional>
#include <map>
using namespace std;
template <typename T>
struct compare_map_values
{
   typedef typename T::value_type  value_type;
   typedef typename T::mapped_type data_type;
   compare_map_values (const data_type& v) : _v (v) {}
   inline bool operator () (const value_type& cur) const
   {
      return cur.second == _v;
   }
private:
   const data_type& _v;
};
int
main ()
{
  typedef map<int, float> myMap_t;
  myMap_t myMap;
  myMap.insert (myMap_t::value_type (0, 1.0f));
  myMap.insert (myMap_t::value_type (1, 2.0f));
  myMap.insert (myMap_t::value_type (2, 3.0f));
  compare_map_values<myMap_t> cmpr_to (2.0f);
  myMap_t::iterator last = std::remove_if (myMap.begin (), myMap.end
(), cmpr_to);
  myMap.erase (last, myMap.end ());
}
// ---------------------------------------------------------------
Che è carino, ma non funziona (questo nemmeno compila, giustamente).
Funzionerebbe su una list, un vector o un altro contenitore che non
debba mantenere per forza l'ordine degli elementi; qui non funziona
perché il primo elemento di map<...>::value_type è costante, e quindi
non può essere assegnato (poiché map è un sorted container, e quindi
le chiavi debbono essere sempre ordinate in modo crescente, mentre
remove_if è veloce, ma sposta gli elementi del contenitore che devono
essere rimossi in coda).
Ciao,
Matteo

 effettuare la cancellazione per chiave.
Infatti... so che è una maialata, ma spesso per
evitare costrutti assurdi mi alloco un normale array
con new, ci copio il contenuto della map o altro che
sia, e certe operazioni mi viene più naturale farle
nell'array. Per questo quasi tutti gli enti della
STL li alloco dinamicamente, tanto so che spesso mi
conviene distruggerli e ricrearli dopo passaggi
intermedi. So che è un mio limite, ma penso che
occorre ammettere che certi costrutti del C++ sono
illeggibili e goffi. Prezzo che pago stra-volentieri
visti i vantaggi su tanti altri fronti.
ciao!
Apx.
-- 
"La teoria è sapere come funzionano le cose e
non sapere come farle funzionare; la pratica è
sapere come far funzionare la cose senza sapere come
funzionano; il nostro scopo è unire la pratica
alla teoria per riuscire a non far funzionare
le cose senza capire come abbiamo fatto"

 illeggibili e goffi.
Mah, non sono perfettamente d'accordo su questo. Semplicemente, ad
ogni impiego il proprio contenitore.
Bisogna saper bene scegliere. Da un punto di vista algoritmico, le map
sono esplicitamente pensate per offrire accesso in tempo O(log(N)). Le
nuove unordered_map, per esempio, offrono accesso in tempo costante O
(1). Chiaro che se devo operare sui valori e non sulle chiavi, devo
fare più fatica; questo per offrire la massima performance su un
contenitore che è pensato per lavorare sulle chiavi.
Se uno deve lavorare molto sui valori, gli conviene usare una map tra
chiavi e *puntatori* ad elementi, che poi vengono immagazzinati
effettivamente in un vector/una list/etc.
Conviene naturalmente scriversi la propria classe wrapper, per
assicurarsi che la vita della map coincida con il ciclo di vita del
vector.
Ciao,
m.


secondo me funziona, dalla documentazione:
http://www.sgi.com/tech/stl/Map.html
Map has the important property that inserting a new element into a map
does not invalidate iterators that point to existing elements. Erasing
an element from a map also does not invalidate any iterators, except, of
course, for iterators that actually point to the element that is being
erased.
Quindi quando esegui erase solo l'iterator tmp non e` piu` valido, ma
itr risulta ancora valido.
Farei comunque in questo modo :
{
	const map<?,?>::iterator e = myMap.end();
	for (map<?,?>::iterator it = myMap.begin(); it != e; )
	{
		if (it->second == target)
		{
			const map<?,?>::iterator w = it;
			++it;
			myMap.erase(w);
		}
		else
			++it;
	}
}
in questo modo eviti di fare la copia dell'iteratore se non e` richiesta
la rimozione dell'elemento

 Semplicemente, ad ogni impiego il proprio contenitore.
E' ovvio che sia cos�, ci mancherebbe. Vi vedo discutere a
livelli che mi posso scordare ora oggi e domani, con costrutti e
soluzioni che solo a posteriori riconosco essere permessi dalla
grammatica ma ai quali non avrei pensato mai. La cosa per me si
complica perch� non smanetto bene coi typedef e mi incarto ben
presto. So che la normalit� non sono io.
ciao!
Apx.

 presto. So che la normalit� non sono io.
No. La normalita' sei tu. C++ e' un linguaggio per alieni e senza
un'eccellente conoscenza di un linguaggio complicatissimo ti escludi
dalle parti migliori. Certi pezzi di boost sono per esempio
inaccessibili a gran parte dei programmatori C++. 
Amen. La cosa non mi piace, ma e' cosi'. Ora non voglio metterla tragica
e non voglio attaccare C++, ma voglio tranquillizzarti e rincuorarti.
C++ *non* e' semplice. Non e' pensato per esserlo e un gruppo di
brillanti pazzoidi ne ha spinto determinate possibilita' al limite. E'
un pregio e un difetto. Conviviamoci.
Poi nel 2024, quando i compilatori supporteranno agevolmente C++ 0x
sara' tutto piu' facile. :)
-- 
-riko

 agevolmente C++ 0x sara' tutto piu' facile. :)
Grazie, effettivamente mi sento rincuorato anche se non � di
quelle faccende per cui si muore (a meno che non sia lavoro).
Sono sempre stato colpito dalla vastit� dello spazio C++. Il
Deitel & Deitel, "tecniche avanzate di programmazione" mi pare
roba per bambini (beninteso, ci trovo tante cose che non so,
parlo dell'impostazione, soprattutto sul lato "algoritmi")
dall'altro non riesco nemmeno a capire a cosa potrebbe servirmi
Boost, figuriamoci comprendere il codice...
Ma dici per scherzo o � in cantiere un'evoluzione del C++?????
ciao!
Apx.

> Ma dici per scherzo o è in cantiere un'evoluzione del C++?????
http://en.wikipedia.org/wiki/C%2B%2B0x
Pleg

 la rimozione dell'elemento
Giusta osservazione, thx!
Pleg

 Ma dici per scherzo o � in cantiere un'evoluzione del C++?????
No, non scherzo. Scherzo dicendo che sara' implementata nel 2024,
verosimilmente sara' almeno il 2030. <grin>
-- 
-riko

> Un bel wrapper su BLAS.
Interessante, non lo sapevo. Ma non ha un wrapper per LAPACK, giusto?
(non l'ho trovato con google)
Pleg

 algoritmi sui grafi).
grazie! Visto che sono migrato a mingw e a quanto
pare non fa i disastri che faceva col vecchio
borland, è ora che mi ammoderno. ^_^


 Interessante, non lo sapevo. Ma non ha un wrapper per LAPACK, giusto?
La pagina con quello che ha boost c'e'. 
Io non ho visto LAPACK. Magari devi cercare nella documentazione.
-- 
-riko