2008-09-29 8 views
25

J'essaie de trouver la meilleure façon de déterminer si je suis dans la dernière itération d'une boucle sur une carte afin de faire quelque chose comme ce qui suit:Comment puis-je détecter la dernière itération dans une boucle sur 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 
    } 
} 

Il semble y avoir plusieurs façons Pour ce faire: les itérateurs à accès aléatoire, la fonction distance, etc. Quelle est la méthode canonique?

Éditer: pas d'itérateur à accès aléatoire pour les cartes!

+0

+1: Bonne question! –

Répondre

23

Canonical? Je ne prétends pas, mais je vous suggère

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

Edité pour corriger comme suggéré par KTC. (Merci, parfois vous allez trop vite et gâcher les choses les plus simples ...)

+0

Rappelle à tous comment le post-décrément fonctionne à nouveau? ;-) – KTC

+0

Decrement doit être un prédécrément ou le finalIter sera terminé. – Torlack

+1

CAVEAT: Il nécessite des itérateurs bidirectionnels, donc il ne fonctionne pas dans toutes les classes de collection STL. –

14

Cela semble être le plus simple:

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

Les itérateurs définissent-ils réellement l'arithmétique plus avec des entiers? Je ne le pensais pas; mais même si certains d'entre eux le font, je suis certain que vous ne pouviez pas compter dessus pour * chaque * conteneur. –

+0

Cela ne fonctionne pas ... il n'y a pas de correspondance pour l'opérateur [type d'itérateur] + int! – cdleary

+0

Les itérateurs bidirectionnels (quelle carte a) n'ont pas + défini pour le manipuler. – KTC

-2

Vous pouvez juste tirer un élément de la carte avant l'itération, puis effectuez votre « dernière itération » travail hors de la boucle, puis mettre l'élément arrière en la carte. C'est terriblement mauvais pour le code asynchrone, mais considérant que le reste du C++ est mauvais pour la concurrence, je ne pense pas que ce soit un problème. :-)

+0

Les cartes sont commandées - peut-être pensez-vous à un hachage? –

+0

J'avais l'impression que Maps était implémenté par défaut en utilisant des tables de hachage. Je vais corriger la réponse. Merci! –

6

Mark Ransom modifié afin qu'il fonctionne comme prévu.

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

Pourquoi la KTC a-t-elle été marquée? C'est plus correct que celui de Mark. – Torlack

+0

Il n'a pas été marqué. Le questionneur a simplement changé d'avis sur la réponse acceptée. – KTC

+0

Merci pour la correction, je vous ai donné une upvote pour cela. –

0

Un simple, mais efficace, approche:

size_t items_remaining = someMap.size(); 

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

Programme complet:

#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; 
} 

Ce programme donne:

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)); 
} 

Utiliser std :: for_each assurera que la boucle est serrée et précise ... Notez l'introduction de la fonction foo() qui prend un seul argument (le type doit correspondre à ce qui est contenu dans someMap). Cette approche a l'ajout d'ajouter 1 ligne. Bien sûr, si Foo est vraiment petit, vous pouvez utiliser une fonction lambda et se débarrasser de l'appel à & Foo.

+0

Cela ne permet pas de "faire quelque chose pour toutes les itérations" – Gianluca

10

Si vous voulez juste utiliser un ForwardIterator, cela devrait fonctionner:

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

Voici mon optimisé prendre:

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

Surpris ne dit encore, mais de boost a bien sûr quelque chose;

Boost.Next (et l'équivalent Boost.Avant)

Votre exemple ressemblerait à ceci:

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

Je suis assez sûr que c'est dans la bibliothèque standard comme 'std :: next', etc. maintenant. –

3

Le code suivant serait optimisé par un compilateur de sorte que, pour être la meilleure solution pour cette tâche par la performance, ainsi que par les règles de POO:

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

C'est le meilleur code des règles de POO parce que std :: carte a une fonction de membre spécial rbegin pour le code comme:

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

Pourquoi travailler pour trouver l'EOF afin de ne rien donner.

Simplement, excluez-le;

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

BAISER (KEEP IT SIMPLE SIMPLEMENT)

+0

Etes-vous sûr que cela fonctionne? Est-ce que les itérateurs définissent 'operator-' avec 'int's? –

0

Que diriez-vous cela, personne ne mentionnant mais ...

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 
    } 
} 

cela semble simple, mm ...

12

Depuis C++ 11, vous pouvez également utiliser 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 
     } 
    } 

Bien que la question ait été posée il y a quelque temps, j'ai pensé que ça valait la peine d'être partagé.

Questions connexes