I'm looking for a std container like a std::list that can efficiently move an element to the front:
a-b-c-d-e
move "b" to front:
a-c-d-e-b
There is no such function in the std containers. Therefor, I think I must combine a remove and push_front function but has anyone can find a better idea?
Thank in advance.
If you don't have to maintain the order of the other elements,
then the simplest solution is doubtlessly just to swap the
element you want with the first element in the container. This
will be efficient with all containers.
Otherwise, std::list offers a splice operation which could
be used. Something like the following, I think:
void
moveToFront(
std::list<MyType>& list,
std::list<MyType>::iterator element )
{
if ( element != list.begin() ) {
list.splice( list.begin(), list, element, std::next( element ) );
}
}
This should end up with only a couple of pointer operations, and
no copies. On the other hand, std::list can be very slow in
general (because of its poor locality); I'd measure very
carefully against the naïve implementation using std::vector,
to make sure it was a win globally. Eliminating all copies here
may not be a win if iterating to find the element you want to
move to the front is ten time more expensive. (A lot of this
depends on how expensive MyType is to copy, and how large it
is. If sizeof(MyType) is close to the size of a page, or
accessing MyType ends up accessing a lot of indirectly
allocated objects, the locality argument won't hold.)
With an std::vector, rather than the obvious erase/insert
void
moveToFront(
std::vector<MyType>& list,
std::vector<MyType>::iterator element )
{
MyType tmp( *element );
std::copy_backwards( list.begin(), std::prev( element ), element );
*list.begin() = tmp;
}
This will result in less copies than the erase (which copies
all of the following elements) insert (which also copies all
of the following elements—which means all of the elements,
because we are inserting at the beginning) pattern.
On std::vector, you could use std::rotate, which has linear complexity
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
std::vector<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
// swap ranges [1, 2) and [2, 5)
auto it = std::next(v.begin(), 1); // O(1)
auto rb = std::next(it);
auto re = v.end();
std::rotate(it, rb, re); // O(N)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
On a std::list you could use the member function splice, which (given iterators) has constant complexity
#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>
std::list<int> v = { 0, 1, 2, 3, 4 };
int main()
{
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
auto it = std::next(v.begin(), 1); // O(N)
auto rb = std::next(it);
auto re = v.end();
v.splice(it, v, rb, re); // O(1)
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ","));
std::cout << "\n";
}
NOTE: the last element is conventially denoted as back in the STL containers, and the first element as front. For std::vector, getting iterators to a certain element is constant time, and swapping is linear time. For std::list, getting iterators is linear time, but splicing into the same list is constant time. However, the much better memory caching behavior of vector is also important as this benchmark by Stroustrup shows.
UPDATE: Several commenters mentioned simply swapping elements: this only applies if you want to transform a-b-c-d-e into a-e-c-d-b. In that case, use std::iter_swap on any container you like. For the transformation of a-b-c-d-e into a-c-d-e-b, use std::rotate or list::splice.
Related
I have 3 sets containing words.
a: car, boat, table, ball
b: car, goat, helicopter
c: square, car, goat, boat
I need to create a vector or set with words that are contained ONLY in set a.
So the answer would be:
result: table, ball
I tried to make it using set_difference and set_intersection but no luck so far. Can you suggest me something?
I tried
set_difference(a.begin(), a.end(), b.begin(), b.end(), res.begin());
set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());
But the result is empty
Your mistake is here:
set_difference(res.begin(), res.end(), c.begin(), c.end(), res.begin());
// ^ ^ ^
You iterate over res and write the result in the same set. You need another set to store the result.
A solution would be:
std::set<std::string> a {"car", "boat", "table", "ball"};
std::set<std::string> b {"car", "goat", "helicopter"};
std::set<std::string> c {"square", "car", "goat", "boat"};
std::set<std::string> tmp;
std::set<std::string> res;
// Difference between a and b --> stored in tmp
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));
// Difference between tmp and c --> stored in res
std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));
for(const std::string & s : res)
std::cout << s << '\n';
Output:
ball
table
Live example
Note: If we look at the documentation of std::set_difference, we can see:
Copies the elements from the sorted range [first1, last1) which are not found in the sorted range [first2, last2) to the range beginning at d_first.
The resulting range is also sorted. Equivalent elements are treated individually, that is, if some element is found m times in [first1, last1) and n times in [first2, last2), it will be copied to d_first exactly std::max(m-n, 0) times. The resulting range cannot overlap with either of the input ranges.
emphasis mine
So if you want to use another container that does not guarantee the uniqueness of its elements (for example std::vector), you need to ensure that each element does not appear several times in your container by yourself.
Note 2: If you don't want to bother with the tmp set (which is useless after getting the res set), you can put it inside a bloc so that it will be destroyed afterwards:
std::set<std::string> res;
{
std::set<std::string> tmp;
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(tmp, tmp.begin()));
std::set_difference(tmp.begin(), tmp.end(), c.begin(), c.end(), std::inserter(res, res.begin()));
} // tmp destroyed here
Live example
Without sharing your code, we can only guess as to what your code is doing wrong.
Here's what I did. I wrapped the difference logic in a helper operator-. I used std::unordered_set intentionally, because they can't be used directly in std::set_difference.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_set>
#include <vector>
using std::cout;
using std::inserter;
using std::ostream;
using std::set_difference;
using std::sort;
using std::string;
using std::unordered_set;
using std::vector;
namespace {
unordered_set<string> operator-(unordered_set<string> const& minuend, unordered_set<string> const& subtrahend) {
vector<string> m(minuend.begin(), minuend.end());
vector<string> s(subtrahend.begin(), subtrahend.end());
sort(m.begin(), m.end());
sort(s.begin(), s.end());
unordered_set<string> diff;
set_difference(m.begin(), m.end(), s.begin(), s.end(), inserter(diff, diff.begin()));
return diff;
}
ostream& operator<<(ostream& out, unordered_set<string> const& container) {
char const* sep = " ";
out << "{";
for (auto const& s : container) {
out << sep << "\"" << s << "\"";
sep = ", ";
}
out << " }";
return out;
}
}
int main() {
auto a = unordered_set<string>{ "car", "boat", "table", "ball" };
auto b = unordered_set<string>{ "car", "goat", "helicopter" };
auto c = unordered_set<string>{ "square", "car", "goat", "boat" };
auto d = a - b - c;
cout << d << "\n";
}
UPDATE answering Fareanor's questions
Why do you use std::unordered_set (instead of std::set)?
I chose the unordered_set to demonstrate that the set_difference required a sorted container. The unordered_set lacks that feature.
And the original poster, in the original unedited question, did not provide the details of what kind of container was being used.
Why do you convert it into a std::vector that will need to be sorted (instead of converting into std::set)?
A vector is a very efficient container, since the elements in it have a locality, and therefore good cache. It is my go-to container.
A set has a lot more memory allocations because it is a mesh of nodes, and lacks the locality.
The string object being contained may lack the locality anyway, since it is basically a smart pointer to a character array. But because of small string optimization (SSO) and these are small strings, it won't also be allocating off the heap.
In the original poster's scenario, there are only a few items in each of the container, so the efficiency concerns are negligible. But worth thinking about, if the problem domain was scaled up.
I think you should have used std::set (without any clarification from the OP at least) and if the user got a std::unordered_set instead, it is up to him to convert it to a proper std::set and then call your operator-().
That is a viable option. Lacking the context at the time, I considered this a "worse case scenario" because the unordered_set container does not fulfill the requirement of the set_difference algorithm.
I have a vector of std::unique_ptr<Foo> objects. I want to get a collection of all vector items that match some condition.
I see the std functions but they all seem to test for a predicate (and return bool) or return a single element.
Is there a built-in mechanism to get a collection that's a subset of a vector? If not, is there a way to construct an iterator that tests items against an arbitrary predicate (to identify ones that meet my condition) and a mechanism to return all items that meet that predicate?
Be warned, since you've got a vector of unique_ptr, those elements can only be moved around, i.e. once you have got the subset, the original vector will not be the same anymore.
The least destructive method is to use std::stable_partition to divide the vector into two groups, while keeping everything in the same vector:
auto sep = std::stable_partition(vec.begin(), vec.end(), [](const auto& foo) {
return foo->is_good();
});
// the part `vec.begin() .. sep` contains all "good" foos.
// the part `sep .. vec.end()` contains all "bad" foos.
If order is not important, use std::partition instead. The usage is the same.
If you want to split the bad foos into another vector, you could use std::copy_if + std::make_move_iterator to move the objects out. Note that this will leave holes everywhere. Use std::remove to clean them up.
decltype(vec) bad_vec;
std::copy_if(std::make_move_iterator(vec.begin()),
std::make_move_iterator(vec.end()),
std::back_inserter(bad_vec),
[](const auto& p) { return !p->is_good(); });
auto new_end = std::remove(vec.begin(), vec.end(), nullptr);
vec.erase(new_end, vec.end());
If you no longer care about the "bad" objects, use std::remove_if:
auto new_end = std::remove_if(vec.begin(), vec.end(), [](const auto& foo) {
return !foo->is_good();
});
vec.erase(new_end, vec.end());
// now `vec` only contains "good" foos.
If you just want to get the raw pointers, instead of the unique_ptr itself, you could use std::transform to fill up a vector<Foo*> and then remove_if to filter it... But at this point it is probably just easier to write the for loop.
std::vector<int*> good_vec;
for (const auto& foo : vec) {
if (foo->is_good()) {
good_vec.push_back(foo.get());
}
}
Since your vector holds unique_ptr's (which we don't make copies of) - I'd recommend the second option you inquired about: An iterator which only iterates those elements matching your predicate. This is exactly boost::filter_iterator.
Sort-of-an example:
bool points_to_positive(int* ptr) {
return ptr != nullptr and *ptr > 0;
}
// ...
std::vector<std::unique_ptr<int>> vec;
// ...
auto iterator = boost::make_filter_iterator(
&points_to_positive, std::begin(vec), std::end(vec)
);
if, however, you plan on making that iteration multiple times, and do not want to trade time for space, you would probably be better served by just copying out the actual pointers like in #kennytm's last suggested option.
What you asked for is std::copy_if from <algorithm>. For unique_ptr elements, which cannot be copied, this is not what you want. Sample code:
#include <algorithm>
#include <array>
#include <cstdlib>
#include <experimental/array>
#include <iostream>
#include <type_traits>
#include <vector>
using std::cout;
using std::endl;
using std::size_t;
bool is_even( const int n )
{
// True iff n is even.
return n % 2 == 0;
}
std::ostream& operator<< ( std::ostream& os, const std::vector<int>& container )
{
// Boilerplate instrumentation.
for ( const int& x : container )
os << x << ' ';
return os;
}
int main(void)
{
// Our input array, raw:
constexpr int digits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// The number of input elements:
constexpr size_t ndigits = std::extent<decltype(digits)>();
// Container wrapping our input array:
constexpr std::array<int, ndigits > numbers =
std::experimental::to_array(digits);
std::vector<int> even_numbers;
even_numbers.reserve(ndigits); // Upper bound on output size.
std::copy_if( numbers.cbegin(),
numbers.cend(),
std::back_inserter(even_numbers),
is_even );
even_numbers.shrink_to_fit();
// Correct output is "2 4 6 8 "
cout << even_numbers << endl;
return EXIT_SUCCESS;
}
However, your array contains unique_ptr objects, which can’t be copied. Several answers have other good suggestions to get equivalent results. If you want to copy the references meeting the requirements to a different collection, though, you could also change unique_ptr to shared_ptr or weak_ptr, which can be copied.
For this code:
std::vector<int> v = {...};
std::sort(v.begin(), v.end());
// Does this work?
std::size_t unique_count = std::unique(v.begin(), v.end()) - v.cbegin();
In the last line, I think since std::unique just moves stuffs around inside the vector and does not insert anything into it, no iterators should be invalidated, and so the way I'm calculating unique_count should be correct. But I want to make sure that is the case. Is it?
std::unique is an algorithm. All stl algorithms operate on ranges, not containers.
Although the algorithm may swap element contents, the iterators to those elements remain unchanged.
This is a guarantee.
If it were not, then this could not work:
#include <algorithm>
#include <vector>
#include <iostream>
#include <array>
int main()
{
auto unique_size = [](auto&& container)
{
std::sort(std::begin(container), std::end(container));
return std::unique(std::begin(container), std::end(container)) - std::cbegin(container);
};
std::cout << unique_size(std::vector<int> {6,5,4,4,3,2,1}) << std::endl;
std::cout << unique_size(std::array<int,7> {6,5,4,4,3,2,1}) << std::endl;
int x[] = {6,5,4,4,3,2,1};
std::cout << unique_size(x) << std::endl;
// Does this work? yes.
}
mandated output:
6
6
6
std::unique return an iterator to one position past the last 'unique' element in the container.
auto last = std::unique(v.begin(), v.end());
Then the range [last, v.end()) contains whatever, you can't rely on v.cbegin(). Instead:
auto unique_count = std::distance(v.begin(), last);
will do the trick.
http://en.cppreference.com/w/cpp/algorithm/unique
I am trying to understand STL algorithms.
Copy is defined as :
template<class InputIterator, class OutputIterator>
OutputIterator copy ( InputIterator first, InputIterator last, OutputIterator result )
Can some one please explain why does the following works when vectors & deques are mixed but fails when vectors and sets are mixed.
#include <iostream>
#include <algorithm>
#include <vector>
#include <deque>
#include <deque>
#include <set>
using namespace std;
int main () {
int myints[]={10,20,30,40,50,60,70};
vector<int> myvector;
vector<int>::iterator it;
set<int> mset(myints,myints+8);
set<int>::iterator setItr = mset.begin();
deque<int> deq;
deq.resize(10);
deque<int>::iterator deqItr = deq.begin();
myvector.resize(7); // allocate space for 7 elements
copy ( myints, myints+7, myvector.begin() );
copy ( myvector.begin(), myvector.end(), deqItr );
cout << "deque contains:";
for (deque<int>::iterator dit=deq.begin(); dit!=deq.end(); ++dit)
cout << " " << *dit;
cout << endl;
//copy ( myvector.begin(), myvector.end(), setItr );
return 0;
}
I understand vectors/deque have random access iterators, where as set's have bidirectional iterators. I fail to understand why compilation fails when only a input/output iterators are required.
PS : This is just an experiment to increase my understanding :)
Associative containers (in plain C++03) are special containers that keep their elements sorted at all times, commonly implemented as a Red Black Tree. To maintain the order invariant, the set and map iterators provide constant references into the key object, and as such you cannot modify it.
In particular for std::set<T>, the iterator will usually be such that std::iterator_traits< std::set<T>::iterator >::reference is const T&, and as such the assignment implicit in the std::copy operation will fail.
If what you want to do is insert the elements into a set, you can use iterators from the <iterator> header that will perform insert operations in the set:
std::copy( v.begin(), v.end(), std::inserter( s, s.end() ) ); // s is the set
std::vector and std::deque have a way to preallocate space. std::set doesn't. Without preallocating the space, attempting to dereference the iterator you pass to copy produces undefined behavior.
The obvious alternative is to use insert iterators instead -- though, unfortunately, you still neednearly always use different code for a set than a deque or vector:
std::copy(myvector.begin(), myvector.end(), std::back_inserter(mydeque));
std::copy(myvector.begin(), myvector.end(), std::inserter(mySet, mySet.end());
It works for vector and deque because you can allocate space beforehand. With other containers, like map, you need an iterator adapter to do that for you. Look at insert_iterator, for example.
I have a source container of strings I want to remove any strings from the source container that match a predicate and add them into the destination container.
remove_copy_if and other algorithms can only reorder the elements in the container, and therefore have to be followed up by the erase member function. My book (Josuttis) says that remove_copy_if returns an iterator after the last position in the destination container. Therefore if I only have an iterator into the destination container, how can I call erase on the source container? I have tried using the size of the destination to determine how far back from the end of the source container to erase from, but had no luck. I have only come up with the following code, but it makes two calls (remove_if and remove_copy_if).
Can someone let me know the correct way to do this? I'm sure that two linear calls is not
the way to do this.
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
vector<string> Container;
Strings.push_back("123");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
remove_copy_if(Strings.begin(), Strings.end(),
back_inserter(Container),
Pred);
Strings.erase(remove_if(Strings.begin(), Strings.end(),
not1(Pred)), Strings.end());
cout << "Elements beginning with 1 removed" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
cout << "Elements beginning with 1" << endl;
copy(Container.begin(), Container.end(),ostream_iterator<string>(cout,"\n"));
return 0;
}
With all due respect to Fred's hard work, let me add this: the move_if is no different than remove_copy_if at an abstract level. The only implementation level change is the end() iterator. You are still not getting any erase(). The accepted answer does not erase() the matched elements -- part of the OP's problem statement.
As for the OP's question: what you want is an in-place splice. This is possible for lists. However, with vectors this will not work. Read about when and how and why iterators are invalidated. You will have to take a two pass algorithm.
remove_copy_if and other algorithms can only reorder the elements in the container,
From SGI's documentation on remove_copy_if:
This operation is stable, meaning that the relative order of the elements that are copied is the same as in the range [first, last).
So no relative reordering takes place. Moreover, this is a copy, which means the elements from Source vector in your case, is being copied to the Container vector.
how can I call erase on the source container?
You need to use a different algorithm, called remove_if:
remove_if removes from the range [first, last) every element x such that pred(x) is true. That is, remove_if returns an iterator new_last such that the range [first, new_last) contains no elements for which pred is true. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove_if is stable, meaning that the relative order of elements that are not removed is unchanged.
So, just change that remove_copy_if call to:
vector<string>::iterator new_last = remove_if(Strings.begin(),
Strings.end(),
Pred);
and you're all set. Just keep in mind, your Strings vector's range is no longer that defined by the iterators [first(), end()) but rather by [first(), new_last).
You can, if you want to, remove the remaining [new_last, end()) by the following:
Strings.erase(new_last, Strings.end());
Now, your vector has been shortened and your end() and new_last are the same (one past the last element), so you can use as always:
copy(Strings.begin(), Strings.end(), ostream_iterator(cout, "\"));
to get a print of the strings on your console (stdout).
I see your point, that you'd like to avoid doing two passes over your source container. Unfortunately, I don't believe there's a standard algorithm that will do this. It would be possible to create your own algorithm that would copy elements to a new container and remove from the source container (in the same sense as remove_if; you'd have to do an erase afterward) in one pass. Your container size and performance requirements would dictate whether the effort of creating such an algorithm would be better than making two passes.
Edit: I came up with a quick implementation:
template<typename F_ITER, typename O_ITER, typename FTOR>
F_ITER move_if(F_ITER begin, F_ITER end, O_ITER dest, FTOR match)
{
F_ITER result = begin;
for(; begin != end; ++begin)
{
if (match(*begin))
{
*dest++ = *begin;
}
else
{
*result++ = *begin;
}
}
return result;
}
Edit:
Maybe there is confusion in what is meant by a "pass". In the OP's solution, there is a call to remove_copy_if() and a call to remove_if(). Each of these will traverse the entirety of the original container. Then there is a call to erase(). This will traverse any elements that were removed from the original container.
If my algorithm is used to copy the removed elements to a new container (using begin() the original container for the output iterator will not work, as dirkgently demonstrated), it will perform one pass, copying the removed elements to the new container by means of a back_inserter or some such mechanism. An erase will still be required, just as with remove_if(). One pass over the original container is eliminated, which I believe is what the OP was after.
There will be copy_if and remove_if.
copy_if( Strings.begin(), Strings.end(),
back_inserter(Container), not1(Pred) );
Strings.erase( remove_if( Strings.begin(), Strings.end(), not1(Pred) ),
Strings.end() );
It is better to understand code where Predicate class answering "true" if something is present. In that case you won't need not1 two times.
Because std::find looks for substring not obligatory from the begin you need to change "beginning with 1" to "with 1" to avoid future misunderstanding of your code.
The whole reason why the remove_* algorithms do not erase elements is because it is impossible to "erase" an element by the iterator alone. You can't get container by iterator
This point is explained in more details in the book "Effective STL"
Use 'copy_if', followed by 'remove_if'. remove_copy_if does not modify the source.
On lists you can do better - reordering followed by splice.
If you don't mind having your strings in the same container, and having just an iterator to separate them, this code works.
#include "stdafx.h"
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
vector<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
cout << "Elements matching with 1" << endl;
copy(end1, Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(Strings.begin(), end1, ostream_iterator<string>(cout,"\n"));
return 0;
}
remove*() don't relally remove elements, it simply reorders them and put them at the end of the collection and return a new_end iterator in the same container indicating where the new end is. You then need to call erase to remove the range from the vector.
source.erase(source.remove(source.begin(), source.end(), element), source.end());
remove_if() does the same but with a predicate.
source.erase(source.remove_if(source.begin(), source.end(), predicate), source.end());
remove_copy_if() will only copy the elements NOT matching the predicate, leaving the source vector intact and providing you with the end iterator on the target vector, so that you can shrink it.
// target must be of a size ready to accomodate the copy
target.erase(source.remove_copy_if(source.begin(), source.end(), target.begin(), predicate), target.end());