Как я могу определить последнюю итерацию цикла по 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
    }
}

Кажется, есть несколько способов сделать это: итераторы произвольного доступа, функция distance и т. д. Что такое канонический метод?

Обновлено: нет итераторов произвольного доступа для карт!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
27
0
25 572
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Это кажется самым простым:

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

Действительно ли итераторы определяют арифметический плюс с целыми числами? Я так не думал; но даже если некоторые из них это сделают, я уверен, что вы не можете полагаться на него для контейнера каждый.

Daniel Spiewak 30.09.2008 02:48

Это не работает ... нет совпадения для оператора [тип итератора] + int!

cdleary 30.09.2008 02:51

Для двунаправленных итераторов (которые есть на карте) не определено +, чтобы управлять им.

KTC 30.09.2008 02:56

Определено уменьшение итератора до пустой коллекции?

Tim Stewart 26.10.2008 08:03

@Tim: Если это пустая коллекция, мы никогда не войдем в тело цикла. :-)

cdleary 29.01.2009 23:56

Вы можете просто вытащить элемент из карты до итерации, затем выполнить свою «последнюю итерацию» из цикла, а затем вернуть элемент в в карту. Это ужасно плохо для асинхронного кода, но, учитывая, насколько плох остальной C++ для параллелизма, я не думаю, что это будет проблемой. :-)

Карты ЗАКАЗЫВАЮТСЯ - может вы думаете о хэше?

Mark Ransom 30.09.2008 02:51

У меня создалось впечатление, что Карты были реализованы по умолчанию с использованием хэш-таблиц. Подправлю ответ. Спасибо!

Daniel Spiewak 30.09.2008 03:39
Ответ принят как подходящий

Канонический? Я не могу утверждать это, но я бы предложил

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

Отредактировано, чтобы исправить, как предлагает KTC. (спасибо! Иногда вы делаете слишком быстро и ошибаетесь в самых простых вещах ...)

Декремент должен быть предварительным, иначе finalIter будет концом.

Torlack 30.09.2008 03:00

ПРЕДОСТЕРЕЖЕНИЕ: для этого требуются двунаправленные итераторы, поэтому он не работает во всех классах коллекций STL.

Euro Micelli 30.09.2008 03:08

Вы можете просто сравнить со значением в rbegin ()

Jeff Yates 30.09.2008 03:25

@ffpf, вы не можете, потому что сравнения между прямыми итераторами и обратными итераторами не работают.

cdleary 30.09.2008 03:43

Можно сравнить с - (someMap.rbegin (). Base ()), но это просто усложняет задачу без уважительной причины. (Это было то, что изначально было задумано опубликовать LOL: D)

KTC 30.09.2008 03:45

Конечно, ну! Признаюсь, с момента моей последней крупной работы над C++ прошло совсем немного времени. Удивительно, как быстро вещи покидают мозг, когда ими не пользуются.

Jeff Yates 30.09.2008 08:29

Если карта не изменяется внутри цикла, final_iter следует оценивать вне его. В этом случае карту необходимо проверить на пустоту, чтобы не столкнуться с пугающим неопределенным поведением.

Gorpik 04.04.2009 19:03

Изменен Mark Ransom, так что он действительно работает так, как задумано.

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

Простой, но эффективный подход:

  size_t items_remaining = someMap.size();

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

Полная программа:

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

Эта программа дает:

 1 2 3 4 5 | last: 6

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

Использование std :: for_each гарантирует, что цикл будет плотным и точным ... Обратите внимание на введение функции foo (), которая принимает единственный аргумент (тип должен соответствовать тому, что содержится в someMap). К этому подходу добавлено добавление одной строки. Конечно, если Foo действительно маленький, вы можете использовать лямбда-функцию и избавиться от вызова & Foo.

Это не позволяет «делать что-то для всех итераций».

Gianluca 08.12.2011 17:11

Если вы просто хотите использовать ForwardIterator, это должно сработать:

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

Вот мой оптимизированный вариант:

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

Удивлен, что об этом пока никто не упомянул, но конечно в бусте что-то есть;)

Boost.Next (и эквивалент Boost.Prior)

Ваш пример будет выглядеть так:

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

Я почти уверен, что сейчас он есть в стандартной библиотеке как std::next и т. д.

Fund Monica's Lawsuit 28.05.2016 23:00

Следующий код будет оптимизирован компилятором, чтобы быть лучшим решением этой задачи как по производительности, так и по правилам ООП:

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

Это лучший код по правилам ООП, потому что std :: map имеет специальную функцию-член rbegin для такого кода, как:

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

Зачем работать, чтобы найти EOF, чтобы ничего ему не дать.

Просто исключите это;

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

ПОЦЕЛУЙ (ДЕЛАЙТЕ ЭТО ПРОСТОЙ)

Вы уверены, что это работает? Определяют ли итераторы operator- с int?

Fund Monica's Lawsuit 28.05.2016 23:01

Как насчет этого, никто не упоминает, но ...

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

это кажется простым, мм ...

Начиная с C++ 11, вы также можете использовать 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
        }
    }

Хотя вопрос был задан некоторое время назад, я подумал, что им стоит поделиться.

Для тех, кто любит цикл на основе диапазонов C++ 11:

    for (const auto& pair : someMap) {
      if (&pair != &*someMap.rbegin()) ...
    }

Обратите внимание, что здесь работает только ссылочный тип, а не auto pair.

Другие вопросы по теме