2008-09-29 13 views
25

Estoy tratando de averiguar la mejor manera de determinar si estoy en la última iteración de un bucle sobre un mapa con el fin de hacer algo como lo siguiente:¿Cómo puedo detectar la última iteración en un ciclo sobre std :: map?

for (iter = someMap.begin(); iter != someMap.end(); ++iter) { 
    bool last_iteration; 
    // do something for all iterations 
    if (!last_iteration) { 
     // do something for all but the last iteration 
    } 
} 

Parece que hay varias maneras de hacer esto: iteradores de acceso aleatorio, la función distance, etc. ¿Cuál es el método canónico?

Editar: ¡no hay iteradores de acceso aleatorio para los mapas!

+0

+1: ¡Buena pregunta! –

Respuesta

23

Canonical? No puedo afirmar que, pero me gustaría sugerir

final_iter = someMap.end(); 
--final_iter; 
if (iter != final_iter) ... 

Editado para corregir según lo sugerido por KTC. (¡Gracias! A veces vas demasiado rápido y estropeas las cosas más simples ...)

+0

¿Recuerda a todos cómo funciona de nuevo el decremento posterior? ;-) – KTC

+0

El decremento debe ser un predecremento o el final será el final. – Torlack

+1

CAVEAT: Requiere iteradores bidireccionales, por lo que no funciona en todas las clases de colección STL. –

14

Este parece ser el más simple:

bool last_iteration = iter == (--someMap.end()); 
+0

¿Realmente los iteradores definen aritmética más con enteros? Yo no lo creía; pero incluso si algunos de ellos lo hacen, estoy seguro de que no puede confiar en él para * cada * contenedor. –

+0

Esto no funciona ... no hay ninguna coincidencia para el operador [iterator type] + int! – cdleary

+0

Los iteradores bidireccionales (qué mapa tiene) no tienen + definido para manipularlo. – KTC

-2

Usted puede simplemente tirar de un elemento del mapa antes de la iteración, a continuación, realizar su "última iteración" trabajo fuera del circuito y luego poner el elemento posterior en el mapa. Esto es terriblemente malo para el código asincrónico, pero considerando cuán malo es el resto de C++ para la concurrencia, no creo que sea un problema. :-)

+0

Los mapas están ordenados, ¿quizás estás pensando en un hash? –

+0

Tenía la impresión de que Maps se implementó por defecto usando hashtables. Corregiré la respuesta. ¡Gracias! –

6

Mark Ransom modificado por lo que realmente funciona como se esperaba.

finalIter = someMap.end(); 
--finalIter; 
if (iter != final_iter) 
+0

¿Por qué se marcó el KTC? Es más correcto que el de Mark. – Torlack

+0

No estaba marcado hacia abajo. El que pregunta simplemente cambió de opinión sobre la respuesta aceptada. – KTC

+0

Gracias por la corrección, te di un voto positivo por ello. –

0

Un simple, pero eficaz, de aproximación:

size_t items_remaining = someMap.size(); 

    for (iter = someMap.begin(); iter != someMap.end(); iter++) { 
    bool last_iteration = items_remaining-- == 1; 
    } 
-1

programa completo:

#include <iostream> 
#include <list> 

void process(int ii) 
{ 
    std::cout << " " << ii; 
} 

int main(void) 
{ 
    std::list<int> ll; 

    ll.push_back(1); 
    ll.push_back(2); 
    ll.push_back(3); 
    ll.push_back(4); 
    ll.push_back(5); 
    ll.push_back(6); 

    std::list<int>::iterator iter = ll.begin(); 
    if (iter != ll.end()) 
    { 
     std::list<int>::iterator lastIter = iter; 
     ++ iter; 
     while (iter != ll.end()) 
     { 
     process(*lastIter); 
     lastIter = iter; 
     ++ iter; 
     } 
     // todo: think if you need to process *lastIter 
     std::cout << " | last:"; 
     process(*lastIter); 
    } 

    std::cout << std::endl; 

    return 0; 
} 

Este programa se obtiene:

1 2 3 4 5 | last: 6 
1
#include <boost/lambda/lambda.hpp> 
#include <boost/lambda/bind.hpp> 
#include <algorithm> 

using namespace boost::lambda; 

// call the function foo on each element but the last... 
if(!someMap.empty()) 
{ 
    std::for_each(someMap.begin(), --someMap.end(), bind(&Foo, _1)); 
} 

El uso de std :: for_each se asegurará de que el lazo está apretado y preciso ... Tenga en cuenta la introducción de la función foo() que toma un solo argumento (el tipo debe coincidir con lo que está contenido en someMap). Este enfoque tiene la adición de ser 1 línea. Por supuesto, si Foo es realmente pequeño, puede usar una función lambda y deshacerse de la llamada al & Foo.

+0

Esto no permite "hacer algo para todas las iteraciones" – Gianluca

10

Si lo que desea es utilizar un ForwardIterator, esto debería funcionar:

for (i = c.begin(); i != c.end();) { 
     iterator cur = i++; 
     // do something, using cur 
     if (i != c.end()) { 
       // do something using cur for all but the last iteration 
     } 
} 
0

Esta es mi opinión optimizado:

iter = someMap.begin(); 

do { 
    // Note that curr = iter++ may involve up to three copy operations 
    curr = iter; 

    // Do stuff with curr 

    if (++iter == someMap.end()) { 
     // Oh, this was the last iteration 
     break; 
    } 

    // Do more stuff with curr 

} while (true); 
6

sorprende que nadie ha mencionado todavía, pero por supuesto impulso tiene algo;)

Boost.Next (y el Boost equivalente.Con anterioridad)

Su ejemplo se vería así:

for (iter = someMap.begin(); iter != someMap.end(); ++iter) { 
    // do something for all iterations 
    if (boost::next(iter) != someMap.end()) { 
     // do something for all but the last iteration 
    } 
} 
+0

Estoy bastante seguro de que esto está en la biblioteca estándar como 'std :: next', etc. ahora. –

3

El siguiente código se optimiza mediante un compilador de modo que para ser la mejor solución para esta tarea por el rendimiento, así como por las reglas de programación orientada a objetos:

if (&*it == &*someMap.rbegin()) { 
    //the last iteration 
} 

Este es el mejor código de reglas de programación orientada a objetos, porque std :: mapa tiene un especial rbegin función miembro para el código como:

final_iter = someMap.end(); 
--final_iter; 
1

Por qué trabajar para encontrar el EOF para no darle algo.

Simplemente, excluirlo;

for (iter = someMap.begin(); someMap.end() - 1; ++iter) { 
    //apply to all from begin to second last element 
} 

Kiss (KEEP IT SIMPLE SIMPLEMENTE)

+0

¿Estás seguro de que esto funciona? ¿Los iteradores definen 'operator-' con 'int's? –

0

¿Qué hay de esto, nadie mencionar pero ...

for (iter = someMap.begin(); iter != someMap.end(); ++iter) { 
    // do something for all iterations 
    if (iter != --someMap.end()) { 
     // do something for all but the last iteration 
    } 
} 

esto parece sencillo, ... mm

12

Desde C++ 11, también puede usar std :: next()

for (auto iter = someMap.begin(); iter != someMap.end(); ++iter) { 
     // do something for all iterations 
     if (std::next(iter) != someMap.end()) { 
      // do something for all but the last iteration 
     } 
    } 

Aunque la pregunta fue hecha hace un tiempo, pensé que valía la pena compartirla.

Cuestiones relacionadas