Я пытаюсь скопировать карту в вектор пары, чтобы затем отсортировать вектор по члену данных пары second. Я решил это следующим образом:
void mappedWordsListSorter(){
for (auto itr = mappedWordsList.begin(); itr != mappedWordsList.end(); ++itr){
vectorWordsList.push_back(*itr);
}
sort(vectorWordsList.begin(), vectorWordsList.end(), [=](pair<string, int>& a, pair<string, int>& b){return a.second > b.second;});
}
Мне нужно найти способ сделать это без использования необработанного цикла, используя вместо этого стандартную библиотеку. Я встречал множество примеров, когда переносили только ключи или значения карты. Мне нужно скопировать в вектор pairs<string, int>. Как лучше всего это сделать?
@Lorand Кажется, что OP хочет выполнить сортировку на основе значений, а не ключей.
Думаю, точный дубликат. stackoverflow.com/questions/684475
Похоже, этот вопрос следует пометить как дубликат этого, поскольку ответ, предоставленный @NathanOliver, намного лучше, чем любой из ответов на этот вопрос. Обновлено: хотя этот вопрос включает в себя сортировку результата.
@ FrançoisAndrieux Я согласен, эти ответы намного качественнее. Есть ли прецедент пометки старого вопроса как дубликата нового? Редактировать: Там есть. Я пометил старый вопрос как повторяющийся. Ответы казались более низкого качества.
Примечание: когда вы передаете лямбда-запись [] вместо [=] - то, как вы ее используете, технически вызывает огромную ошибку производительности, поскольку передает все доступные переменные копированием (при компиляции Release это должно исключить ненужное копирование, но это может разрушить среду выполнения отладки. ).
@DrewDormann: Я не думаю, что есть что-то о пометке дубликатов, что говорит о том, что новые вопросы должны указывать на старые. Но я недостаточно уверен в политике сайта, чтобы делать это сам, возможно, я что-то упустил.
@ ALX23z - Я согласен, что [=] не должно быть. Но то, что он там, не означает, что все копируется. Лямбда должна использовать что-то из окружающей области, чтобы захватить это.
@ ALX23z На самом деле, он будет копировать переменную только в том случае, если вы используете ее в теле лямбда. Использование [=] не снижает производительности, если вы не используете какие-либо переменные в области видимости.





Вы можете использовать std::copy и std::back_inserter:
std::copy(mappedWordsList.begin(),
mappedWordsList.end(),
std::back_inserter(vectorWordsList));
Честно говоря, мне кажется, что петля range-for понятнее:
for(const auto& kv : mappedWordsList)
vectorWordsList.emplace_back(kv);
Тем не менее, вы можете использовать std::vector::reserve для предварительного выделения памяти на вашем целевом vector, избегая ненужных перераспределений.
Просто используйте функцию-член std::vectorassign.
//no need to call reserve, bidirectional iterators or better will compute the size and reserve internally.
vectorWordsList.assign(mappedWordsList.begin(), mappedWordsList.end());
Если у вас есть существующие значения в векторе, которые вы не хотите перезаписывать, используйте вместо этого insert, например
vectorWordsList.reserve(vectorWordsList.size() + mappedWordsList.size()); // make sure we only have a single memory allocation
vectorWordsList.insert(vectorWordsList.end(), mappedWordsList.begin(), mappedWordsList.end());
Я хотел бы надеяться, что assign по сути будет reserve внутренне.
@ FrançoisAndrieux Скорее всего, это произойдет, если вы передадите итераторы с произвольным доступом, но поскольку map имеет двунаправленные итераторы, требуется обход, чтобы узнать, сколько элементов выделять пространство, поэтому он не собирается этого делать.
@NathanOliver libstdC++ фактически делает reserve внутренне. Для любого ForwardIterator он вызовет std::distance, заплатив за обход чего-либо, кроме RandomAccessIterator. Вы можете отследить std::vector::assign до здесь.
@ Джастин Круто. Спасибо, что нашли это. Я по-прежнему предпочитаю быть явным, если другие реализации этого не делают.
Размещение .resize(0) перед .reserve() может быть оптимизацией, если элементы должны быть заменены.
Поскольку assign не требует MoveInsertable, если итератор является прямым или более сильным, он должен зарезервировать и не должен перераспределяться при задании таких итераторов. Кроме того, во втором случае reserve является антипаттерном (предотвращает экспоненциальный рост и может привести к квадратичному поведению).
reserve по-прежнему не рекомендуется во втором случае, если вы не знаете, что не расширяете вектор дальше. Если я запустил второй фрагмент кода в цикле, он перераспределил бы каждая итерация, тогда как если бы я просто использовал insert без reserve, сработало бы поведение экспоненциального роста, и вы перераспределите только O (logN) раз. Более того, высококачественные реализации должны предварительно вычислять размер вставленного диапазона при наличии прямого или более сильного итератора, поэтому reserve не дает реальной выгоды.
Стоит отметить, что если вы создание вектора для этой цели, вы можете напрямую использовать конструктор вектора:
std::vector<std::pair<FirstType,SecondType>> vectorWordsList( mappedWordsList.begin(), mappedWordsList.end() );
В C++ 17 вы также можете опустить аргументы шаблона вектора, чтобы компилятор мог их вывести:
std::vector vectorWordsList( mappedWordsList.begin(), mappedWordsList.end() );
Если вы не хотите создать вектор итераторов, вам нужны скобки.
@Lorand - сортировка происходит по элементу
secondв паре, который не является ключевым.