keeping an iterator valid while splicing a list C++ - c++

There are a few posts around iterators with lists here using the insert and splice here functions but I am still not able to translate them for my case, I am iterating across a list and if a condition is meet I want to splice (move) the element to another list, but as stated here the iterator jumps to the splices container. How can I keep the iterator related to the original loop as in the example below.
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <ctime>
#include <list>
using namespace std;
class Individual {
public:
Individual(bool state) : state_(state) {}
bool my_state(void) {
return state_;
}
private:
bool state_ = true;
};
int main () {
cout << "----------Enter Main----------" << endl;
list<Individual> list_individuals;
list<Individual> cache_list_individuals;
// initialise
for (auto i = 0; i < 100; ++i) {
if (i <= 50)
list_individuals.push_back(new Individual(true));
else
list_individuals.push_back(new Individual(false));
}
unsigned counter = 0;
for (auto iter = list_individuals.begin(); iter != list_individuals.end(); ++iter, ++counter) {
if ((*iter).my_state()) {
cache_list_individuals.splice(cache_list_individuals.begin(),list_individuals, iter);
// I need to make the iterator related to list_individuals not cache_list_individuals
}
}
cout << "----------Exit Main----------" << endl;
system("PAUSE");
return 0;
}

for (iter = list.begin(); iter != list.end();) {
otherIter = iter++;
if (condition) {
otherList.splice(otherList.cend(), otherIter, list);
}
}
Move incrementing the iterator into the loop.
Using a post increment, moves iter, and keeps it iterating through list, while otherIter is iterating through otherList after the splice().

You could use a copy of your loop iterator to do the splicing:
if ((*iter).my_state()) {
auto splice_iter = iter;
cache_list_individuals.splice(cache_list_individuals.begin(), list_individuals, splice_iter);
}
But after copy both iterators will point to the moved element in the cache_list_individuals list.
David C. Rankin's comment above is where my code was trying to go.

Related

Is it possible to remove elements from a vector of shared_ptr?

Say I have
vector<shared_ptr<string>> enemy;
how do I remove elements from the enemy vector?
Thanks for your help in advance
**Edit (code in context)
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
int i = 0;
bool found = FALSE;
for(auto it = chart.begin(); it != chart.end(); i++)
{
if(id == chart[i]->GetEnemyID() )
{
found = TRUE;
chart.erase(it);
}
}
the code above segfaults me
You remove elements the same way you remove any elements from any std::vector - via the std::vector::erase() method, for instance. All you need for that is an iterator to the desired element to remove.
In your case, since you are storing std::shared_ptr<std::string> objects rather than storing actual std::string objects, you may need to use something like std::find_if() to find the vector element containing the desired string value, eg:
void removeEnemy(string name)
{
auto iter = std::find_if(enemy.begin(), enemy.end(),
[&](auto &s){ return (*s == name); }
);
if (iter != enemy.end())
enemy.erase(iter);
}
UPDATE: in the new code you have added, you are incorrectly mixing indexes and iterators together. You are creating an infinite loop if the vector is not empty, as you never increment the it iterator that controls your loop, you are incrementing your index i variable instead (see what happens when you don't give your variables unique and meaningful names?). So you end up going out of bounds of the vector into surrounding memory. That is why you get the segfault error.
Even though you are (trying to) use an iterator to loop through the vector, you are using indexes to access the elements, instead of dereferencing the iterator to access the elements. You don't need to use indexes at all in this situation, the iterator alone will suffice.
Try this instead:
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
for(auto it = chart.begin(); it != chart.end(); ++it)
{
if (id == it->GetEnemyID() )
{
chart.erase(it);
return;
}
}
Or, using the kind of code I suggested earlier:
void RemoveEnemy( vector<shared_ptr<Enemy>> & chart, string id )
{
auto iter = std::find_if(chart.begin(), chart.end(),
[&](auto &enemy){ return (enemy->GetEnemyID() == id); }
);
if (iter != chart.end())
chart.erase(iter);
}
The problem with your code is that erase() invalidates the iterator. You must use it = chart.erase(it).
I like mine which will remove aliens at high speed and without any care for the ordering of the other items. Removal with prejudice!
Note: remove_if is most often used with erase and it will preserve the order of the remaining elements. However, partition does not care about the ordering of elements and is much faster.
partition-test.cpp:
make partition-test && echo 1 alien 9 alien 2 8 alien 4 7 alien 5 3 | ./partition-test
#include <algorithm>
#include <iostream>
#include <iterator>
#include <memory>
#include <string>
#include <vector>
using namespace std;
template <typename T>
ostream &operator<<(ostream &os, const vector<T> &container) {
bool comma = false;
for (const auto &x : container) {
if (comma)
os << ", ";
os << *x;
comma = true;
}
return os;
}
int main() {
vector<shared_ptr<string>> iv;
auto x = make_shared<string>();
while (cin >> *x) {
iv.push_back(x);
x = make_shared<string>();
}
cout << iv << '\n';
iv.erase(partition(begin(iv), end(iv),
[](const auto &x) { return *x != "alien"s; }),
end(iv));
cout << iv << '\n';
return 0;
}

How to apply the concept of counting occurrences on strings variables in C++

following program ca calculate the frequency of ints in an array
how to apply this concept on string variable because a string is also an array on the back end
using namespace std;
int counter[10]={0,0,0,0,0,0,0,0,0,0};
int arr [9][9],x;
int main()
{
srand(time(NULL));
cout<<"enter the array \n";
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
arr[i][j]=rand()%10;
}
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
cout<<arr[i][j]<<" ";
}
cout<<endl;
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
counter[arr[i][j]]++;
}
}
for(int j=0;j<10;j++){
cout<<j<<" : "<< counter[j]<<endl;
}
return 0;
}
Here is how one can count occurrences of anything from anything:
Code
#include <iterator>
#include <map>
#include <algorithm>
template<class InputIt>
auto
occurrences(InputIt begin, InputIt end)
{
std::map<typename std::iterator_traits<InputIt>::value_type, std::size_t> result;
std::for_each(begin, end, [&result](auto const& item){ ++result[item]; });
return result;
}
Usage
#include <string>
#include <iostream>
int main()
{
auto text = std::string{"Hello, World!"};
auto occ = occurrences(begin(text), end(text));
std::cout << occ['l'] << '\n'; // outputs 3
}
Live demo
Explanation
template<class InputIt>
This is a generic (template) function iterating over any input iterator.
auto
Its return type is inferred from its implementation. Spoiler alert: it is a std::map of (value counter, occurrence of this value).
occurrences(InputIt begin, InputIt end)
occurrences is called with a couple of iterators defining a range, generally calling begin(C) and end(C) on your container C.
std::for_each(begin, end, //...
For each element in the range...
[&result](auto const& item){ //...
...execute the following treatment...
++result[item]; });
...increment the occurrence count for the value item, starting with zero if its the first.
This is not an efficient implementation since it copies the values it counts. For integers, characters, etc. its perfect but for complex types you might want to improve this implementation.
It's generic and standard container compatible. You could count anything iterable.
If I understand correctly, you want to count occurrences of strings. STL container map is useful for this purpose. Following is example code.
#include<iostream>
#include<map>
#include<string>
#include<vector>
int main()
{
std::vector<std::string> arrayString;
std::map<std::string, int> counter;
std::map<std::string, int>::iterator it;
arrayString.push_back("Hello");
arrayString.push_back("World");
arrayString.push_back("Hello");
arrayString.push_back("Around");
arrayString.push_back("the");
arrayString.push_back("World");
// Counting logic
for(std::string strVal : arrayString)
{
it = counter.find(strVal);
if(it != counter.end())
it->second += 1; // increment count
else
counter.insert(std::pair<std::string, int>(strVal, 1)); // first occurrence
}
// Results
for(std::map<std::string, int>::iterator it = counter.begin(); it != counter.end(); ++it)
std::cout << it->first << ": " << it->second << std::endl;
return 0;
}
More compact way to write the counting logic is :
// Counting logic
for(std::string strVal : arrayString)
{
++counter[strVal]; // first time -> init to 0 and increment
}

How do I increment an iterator inside a for loop body c++

I'm trying to join to simple char lists together one element at a time to create a new list. For example, if listA = 1,2,3,4,5 and listB = a,b,c,d,e,f, listC = a,1,b,2,c,3 etc...
I have a function taking in two char list, but I'm not able to increment the iterators from list A and B without receiving a compiler error.
My code is as follows:
void altFusion(std::list<char> listOne, std::list<char> listTwo) {
std::list<char>::iterator iter;
std::list<char>::iterator nextIter;
iter = listOne.begin();
nextIter = listTwo.begin();
std::list<char>newList;
char temp = *iter;
char temp2 = *nextIter;
for (int i = 0; i < 10; i++) {
newList.push_back(temp);
newList.push_back(temp2);
++iter;
++nextIter;
}
std::list<char>::iterator newListIter;
for (newListIter = newList.begin(); newListIter != newList.end(); ++newListIter) {
std::cout << *newListIter;
}
}
If I'm not allowed to increment my iterator inside the body of the for loop, how should I?
If I remove the ++iter and ++nextIter, the program works, but the answer isn't desirable. (a,1,a,1,a,1,a,1...)
Thank you for the help!
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
using namespace std;
std::list<char> listA = {'1','2','3','4','5'};
std::list<char> listB = {'a','b','c','d','e'};
//listC = a,1,b,2,c,3 etc...
int main()
{
std::list<char> new_list;
for (auto iterator = listA.begin(), end = listA.end(); iterator != end; ++iterator)
{
// get current index
int index = std::distance(listA.begin(), iterator);
// insert item from list a
new_list.push_back(*iterator);
// listb item
auto it = listB.begin();
std::advance(it, index);
new_list.push_back( *it);
}
for (auto n : new_list)
{
std::cout << n << std::endl;
}
return 0;
}
the reason why your post increment and pre increment is not working because of this. your just using the same value over and over again. your are not replacing temp and temp2.
newList.push_back(temp);
newList.push_back(temp2);

How do I use two consecutive elements of a deque at once in a for loop?

I have to process element from a deque (first to last), but at each iteraton I need to use one element and the next too. So I'm trying to write a for loop that starts with mydeque.begin() and finishes at mydeque[mydeque.size()-1]. Also; I would like to know if my iterator deque::iterator it has a next method (it->next()), to make operations like *it - *it->next(). Thank you very much.
Here's how:
#include <deque>
#include <iostream>
int main() {
std::deque<int> test = {1,2,3,4,5,6,7,8};
for(auto i = test.begin(); i != test.end(); i++) {
auto next = std::next(i);
std::cout << *i << *next << std::endl;
if(next==test.end())
{
//Do something. This is the last element.
break;
}
}
}
EDIT: Watch out for deques with only one element. Do this by performing a check such as if(test.begin()==test.end()).
EDIT: My initial solution was indeed a bit error prone and unprecise.
This can easily be done using iterators, see this little example:
#include <deque>
#include <iostream>
int main() {
std::deque<int> test = {1,2,3,4,5,6,7};
for(auto i = test.begin(); i != test.end(); ++i) {
auto next = std::next(i);
if(next != test.end()) {
std::cout << *i << " " << *next << std::endl;
}
}
}
You can simply increment the iterator by 2 and use std::next to also pick the following item (since the offset of std::next defaults to 1).

how can i check if the iterator come to the end of list?

i have this code:
and i want on the function run on all over the list and print it
but i don't want to send the list to the function
i want to send only the iterator
but the problem it the on the "for" i cant do
it_mylist != it_mylist.end()
what can i do please?
#include <iostream>
using namespace std;
#include <list>
void f(list<int>::iterator it_mylist)
{
for (; it_mylist != it_mylist.end(); ++it_mylist)
{
cout << *it_mylist;
}
}
void main()
{
list<int> mylist;
mylist.push_front(1); mylist.push_front(2); mylist.push_front(3);
list<int>::iterator it_mylist = mylist.begin();
f(it_mylist);
}
You need to pass a pair of iterators, one to the beginning and the other to the end of your list:
void f(list<int>::iterator first, list<int>::iterator last) {
for (; first != last; ++first) {
cout << *first;
}
}
// In main()
f(begin(mylist), end(mylist));