Correct way to invoke remove_if [duplicate] - c++

This question already has answers here:
std::remove_if not working properly [duplicate]
(4 answers)
Closed 1 year ago.
In the following sample code remove_if is supposed to delete all even numbers but it is not working as I expect. I am obviously doing something wrong since the output continues to show some even numbers, towards the end.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool
myCond(int i) { return i % 2 == 0; }
int
main ()
{
vector<int> myVector = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22};
remove_if(myVector.begin(), myVector.end(), myCond);
for(int i : myVector) cout << i << " ";
cout << endl;
return 0;
}
output 11 13 15 17 19 16 17 18 19 20 22

std::remove_if only move the elements that need to be removed to the end of the container, you still need to use vector::erase to actually erase them.
If your compiler supports C++20, then I recommend using std::erase_if which is more intuitive and less error-prone:
vector<int> myVector = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22};
std::erase_if(myVector, myCond);
Demo.

remove_if returns an iterator - which will be the end of the rearranged items.
You can change you for loop to stop when it gets to the new end.

std::remove_if returns an iterator to the first element removed. In other words, everything before the iterator is evaluated to be false.
Have a look at the reference, especially the Return value paragraph.
For us, this means we have to iterate to returned iterator (the new end) if we want to print the odd values -- or we erase all elements from the returned iterator to end().
#include <iostream>
#include <vector>
#include <algorithm>
bool myCond(int i) { return i % 2 == 0; }
int main () {
std::vector<int> myVector = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22};
auto rm_it = std::remove_if(myVector.begin(), myVector.end(), myCond);
// Possibility 1: Print odd values
auto it = myVector.cbegin();
for (; it != rm_it; ++it) std::cout << *it << " ";
std::cout << std::endl;
// Possibility 2: Erase values from the vector
myVector.erase(rm_it, myVector.end());
for(int i : myVector) std::cout << i << " ";
std::cout << std::endl;
return 0;
}

Related

erasing from vector after remove_if, with reverse iterators: rbegin(), rend()

Consider the following vector:
std::vector<int> arr = {20, 37, 11, 20, 15}
I need to remove the duplicate, that is encounter more than n times (in this example n=1)
The resulting vector should look like:
{20, 37, 11, 15}
i.e. the succeeding elements should be deleted, but not the preceding ones.
I had an idea of using rbegin() and rend() iterators in std::remove_if function to start removing elements from the end, but then I am not sure how to approach the erasing of elements from the vector after remove_if is done. I belienve the "left over garbage" is accumulated in the beginning:
auto new_end = std::remove_if(arr.rbegin(), arr.rend(), [&n, &arr](const int a) {
return std::count(arr.begin(), arr.end(), a) > n;
});
//how to erase garbage?
The functor passed to std::remove_if can have a memory.
Code using a functor
struct remove_duplicates {
std::map<int, int> seen_values_;
bool operator()(int x) {
seen_values_[x]++;
return seen_values_[x] > 1;
}
};
int main() {
std::vector<int> arr = {20, 37, 11, 20, 15};
arr.erase(
std::remove_if(arr.begin(), arr.end(), remove_duplicates()),
arr.end());
for (int x : arr)
std::cout << x << " ";
std::cout << std::endl;
}
Code using a lambda
int main() {
std::vector<int> arr = {20, 37, 11, 20, 15};
std::map<int, int> seen_values;
arr.erase(
std::remove_if(
arr.begin(), arr.end(), [&seen_values](int x) {
seen_values[x]++;
return seen_values[x] > 1;
}),
arr.end());
for (int x : arr)
std::cout << x << " ";
std::cout << std::endl;
}
Output is the same for both
20 37 11 15
A note about std::remove_if
The "left over garbage" actually is at the end. std::remove_if returns an iterator to the first position to start deleting from.

How to select the most common numbers from a vector or on array?(TOP5 toplist) C++

I have a vector with integers. I would like to select the most common numbers. I would like to make a top 5 list. For example:
std::vector<int> numbers = {32, 32, 32, 12, 12, 11, 11, 11, 9};
the most common numbers are: TOP 1: 32, TOP 2: 11, TOP 3: 12, TOP 4: 9;
after I selected them, I would like to store it into an other vector: the most common numbers.
Here's another algorithm, the cost is going to be O(n) for any k, plus a decent cache locality.
1.First store all elements in a unordered_map O(N)
std::unordered_map<int, int> m;
for(const auto ele: numbers) {
m[ele]++;
}
2.Dump all elements in a vector of pairs O(N)
std::vector<std::pair<int, int>> temp;
for(const auto& ele: m) {
temp.emplace_back(ele.first , ele.second);
}
3.Now use the nth_element to find kth rank O(N)
std::nth_element( temp.begin(), temp.begin()+k ,
temp.end(), [](const auto& p1, const auto& p2) {
// Strict weak ordering
if (p1.second > p2.second) {
return true;
} if (p1.second < p2.second) {
return false;
}
return p1.first > p2.first; // We have to print large element first
} );
4.Display the output
std::for_each( temp.begin(), temp.begin() +k - 1, [](const auto & p) {
std::cout << p.first << " ";
});
Demo Here
You can create a unordered_map<int,int> mp where you can store the count of each number, like mp[32] = 3.
Next you need to find the top five elements
Time Complexity : O(mlogm) : You can sort the map in descending order( to sort it you will have to use a extra vector), and take the first 5 elements.
Time Complexity: O(m) : Or you can iterate 5 times over the whole map to get the top file elements. Each time you iterate find the number with the greatest frequency which is not present in our topFive vector yet.
m : number of entries in the map.
I've made this example and put comments in-line. It needs C++11 at least.
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>
int main(void) {
std::map<int, int> ans;
std::vector<int> numbers = {32, 32, 32, 12, 12, 11, 11, 11, 9};
std::vector<std::pair<int, int>> sorted;
std::vector<int> common;
// Step 1 Accumulate into a map, counting occurrences
for (auto number : numbers) {
ans[number]++;
}
// Step 2 Make a linear list, putting the count first then the value
for (auto& ent : ans) {
sorted.emplace_back(ent.second, ent.first);
}
// Step 3 sort it, by count ascending
std::sort(std::begin(sorted), std::end(sorted));
// Step 4 Get commonest 5 (or fewer)
for (int i = 1; i<= 5; ++i) {
int index = sorted.size() - i;
if (index >= 0) {
common.push_back(sorted[index].second);
}
}
// Step 5 print out
for (auto i : common) {
std::cout << i << std::endl;
}
return 0;
}
you can do this: create a set so you get rid off duplicated, then find the frequency of every item of the set in the vector, with this result create a pair (something like int, int) push the pair in a vector and finally sort it using your own predicate:
now for the top x you can do a for loop or just resize the vector if you are sure what the consequences of this are.
std::vector<int> numbers{32, 32, 32, 12, 12, 11, 11, 11, 9};
std::set<int> mySet(numbers.begin(), numbers.end());
std::vector<std::pair<int, int>> result{};
for(const auto& x : mySet)
{
result.push_back(std::make_pair(x , std::count(numbers.begin(), numbers.end(), x)));
}
std::sort(result.begin(), result.end(), [](const std::pair<int, int>& a, const std::pair<int, int>& b){return (b.second < a.second);});
//result.resize(3);
std::cout << "Top: " << std::endl;
for(const auto& x : result)
{
std::cout << x.first << ':' << x.second << std::endl;
}
the result will be:
Top: 11:3 32:3 12:2 9:1
There are many many ways how to achieve this. One of which may be.
std::vector numbers = {32, 32, 32, 12, 12, 11, 11, 11, 9};
int maxNumber = *std::max_element(numbers.begin(), numbers.end())
std::vector<int> occurrences(maxNumber + 1, 0);
for(auto& value : numbers)
{
occurrences[value]++;
}
Then you just need to sort the array whilst keeping track of the indexes. This is a topic of another question C++ sorting and keeping track of indexes
.

c++ How to erase an element without changing its end()

I'm trying to erase an element from a vector by index as follows, but why does the first output differ from the second output? Is there any way of avoiding this or a better way of removing elements?
int main() {
vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << *test.end() << endl;
test.erase(test.begin() + 2);
cout << *test.end() << endl;
return 0;
}
std::vector::end returns the iterator to the element following the last element, dereference on it leads to UB, means anything is possible, it's just meaningless.
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
You might use std::vector::back to get the last element; (and better to check whether the vector is empty or not in advance.)
int main() {
vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << test.back() << endl;
test.erase(test.begin() + 2);
cout << test.back() << endl;
return 0;
}
normally you can not print the *test.end() , because it is not the last pointer, it is after the last pointer. it's value is not valid for you,and may cause an exception.
vector::end() == vector::begin() + vector::size()
you can use vector::back
I checked the stl code of vector::erase()
iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v<value_type>) /* strengthened */ {
const pointer _Whereptr = _Where._Ptr;
auto& _My_data = _Mypair._Myval2;
pointer& _Mylast = _My_data._Mylast;
#if _ITERATOR_DEBUG_LEVEL == 2
_STL_VERIFY(
_Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _My_data._Myfirst && _Mylast > _Whereptr,
"vector erase iterator outside range");
_Orphan_range(_Whereptr, _Mylast);
#endif // _ITERATOR_DEBUG_LEVEL == 2
_Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr);
_Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1)); // here after move it destroy the last element.
--_Mylast;
return iterator(_Whereptr, _STD addressof(_My_data));
}
so use *end() may cause a exception of wild pointer.
try list
#include <vector>
#include <list>
int main() {
list<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto pbeg=test.begin(),
pend=test.end(),
a = pbeg, b = pend;
cout << *(--b) << endl;
++(++a);
test.erase(a);
cout << *(--b) << endl;
for ( a=pbeg; a!=pend; ++a)
cout<< *a <<" ";
return 0;
}
10
10
1 2 4 5 6 7 8 9 10
Your choice of the container needs to be revisited if you want avoid iterator invalidation during erase or removal of items from container.
Below link can provide the overview of iterator invalidation:
http://kera.name/articles/2011/06/iterator-invalidation-rules-c0x/

C++ "select" algorithm

Among the functionalities found in std::algorithm I can't seem to find one of the most basic I can think of: selected a subset of a collection (for example, return all the odd numbers, all the employees that have status == 'employed', all items that cost less that 20 dollars).
So, given a list of ints like
vector<int> ints {1, 9, 3, 27, 5, 19, 3, 8, 2, 12};
vector<int> evens = ?
vector<int> greaterThan7 = ?
How to find those that are even and those that are greater than 7?
If you want something more functional, you can check out the boost range library. Specifically, filtered:
for (int i : ints | filtered([](int i){return i > 7;}))
{
...
}
This gives you a lazy view, without constructing a new container.
You can get the same from Eric Niebler's range-v3:
for (int i : view::filter(ints, [](int i){return i > 7;})
{
...
}
with the benefit that you can just assign that to a vector too (so you can choose if it's lazy or eager, which Boost.Ranges does not allow).
std::vector<int> greaterThan7 = view::filter(ints, [](int i){return i > 7;});
std::vector<int> sameThing = ints | view::filter([](int i){return i > 7;});
For example
vector<int> ints {1, 9, 3, 27, 5, 19, 3, 8, 2, 12};
vector<int> evens;
std::copy_if( ints.begin(), ints.end(), std::back_inserter( evens ),
[]( int x ) { return x % 2 == 0; } );
Here is a demonstrative program
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
int main()
{
std::vector<int> ints { 1, 9, 3, 27, 5, 19, 3, 8, 2, 12 };
std::vector<int> evens;
std::copy_if( ints.begin(), ints.end(), std::back_inserter( evens ),
[]( int x ) { return x % 2 == 0; } );
for ( int x : evens ) std::cout << x << ' ';
std::cout << std::endl;
}
Its output is
8 2 12
Depending on what your exact requirements are, consider std::stable_partition (or std::partition). It reorders elements in the range such that all which satisfy a predicate come first. You can think of it as splitting the range into a "subset" and a "not subset" part. Here is an example:
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
using std::begin;
using std::end;
using std::cbegin;
using std::cend;
std::vector<int> ints { 1, 9, 3, 27, 5, 19, 3, 8, 2, 12 };
auto const greater_than_7 = [](int number) { return number > 7; };
auto const iter_first_not_greater_than_7 = std::stable_partition(begin(ints), end(ints), greater_than_7);
for (auto const_iter = cbegin(ints); const_iter != iter_first_not_greater_than_7; ++const_iter)
{
std::cout << *const_iter << "\n";
}
}
If, however, you are fine with copying each matching element to a new collection, for example because the source range must not be modified, then use std::copy_if.
Perhaps what you are really looking for is a view of an unmodifiable range. In this case, you are approaching the problem from the wrong direction. You don't need a particular algorithm; a more natural solution to the problem would be a filtering iterator, like for example Boost's Filter Iterator. You can use the one in Boost or study its implementation to learn how you could write filtering iterators yourself.

C++ equivalent to the python code: "for x in iterable:" [duplicate]

This question already has answers here:
What is the correct way of using C++11's range-based for?
(4 answers)
Closed 7 years ago.
I want to do in c++ something like in python would be:
nums=[31, 46, 11, 6, 14, 26]
nlttf=[]
for n in nums:
if n<25:nlttf.append(n)
That would be the Range-based for loop:
SomethingIteratable cont;
for (const auto &v : cont) {
// Do something
}
As usual, const auto &v gives you immutable references, auto &v mutable references and auto v mutable deep copies.
Beware: You may not do anything in the loop that invalidates iterators of the container you iterate.
If you have C++11 then the code is the following:
for (int n: iterator) { /*Do stuff with n*/ }
If you have C++11 or greater, then you can do it this way.
#include <iostream>
#include <vector>
using namespace std;
int main() {
int num[] = {31, 46, 11, 6, 14, 26};
vector<int>nlttf;
for(int n:num){
if(n<25)nlttf.push_back(n);
}
return 0;
}
Read this Range-based for Statement (C++).
For std::vector see this and this link.
Here's another option, using C++11 and the boost libraries:
#include <iostream>
#include <boost/foreach.hpp>
#include <vector>
int main(){
std::vector<int> nums {31, 46, 11, 6, 14, 26};
std::vector<int> nltff;
BOOST_FOREACH(auto n, nums) if (n < 25) nltff.push_back(n);
BOOST_FOREACH(auto n, nltff) std::cout << n << " ";
std::cout << std::endl;
return 0;
}
Output:
11 6 14