Want to know the most efficient way to map ranges of values in C++
When for example:
I have a bunch of objects differentiated by integer key ranges, e.g.
0-10 -> object a
11-20 -> object b
21-30 -> object c
Where objects are a particular class with a few variables of their own inside. There should be a thousand objects in this scenario.
I was wondering what is the best/fastest way in C++ STL to lookup an object based on an input key. e.g.
lookup(13) -> object b
Note that the ranges are not fixed and may not be of equal size.
Thanks
Easily accomplished with std::map and it's upper_bound function.
Use the lower end of each range as the key into a map. The corresponding value of the map type is a triple of {lower bound, upper bound, and item}. Then to lookup an object based on a specific value, invoke map::upper_bound and to find the the item in the map that is "one past" the matching item. Then "go back 1" and test to see if it's a match.
#include <algorithm>
#include <iostream>
#include <map>
template <typename T>
struct RangeAndValue
{
int low;
int high;
T value;
};
template <typename T>
struct RangeTable
{
std::map<int, RangeAndValue<T>> table;
void Insert(int low, int high, const T& t)
{
table[low] = {low, high, t};
}
bool Lookup(int value, T& t)
{
auto itor = table.upper_bound(value);
if (itor != table.begin())
{
itor--;
if ((value >= itor->second.low) && (value <= itor->second.high))
{
t = itor->second.value;
return true;
}
}
return false;
}
};
Proof of concept (using your sample ranges of 0-10 maps to a, 11-20 maps to b, and 21-30 maps to c)
int main()
{
RangeTable<std::string> rangeTable;
rangeTable.Insert(0, 10, "a");
rangeTable.Insert(11,20, "b");
rangeTable.Insert(21,30, "c");
for (int i = -1; i < 32; i++)
{
std::string s;
bool result = rangeTable.Lookup(i, s);
std::cout << i << " : " << (result ? s : "<not found>") << std::endl;
}
return 0;
}
Produces expected results when run:
$ g++ main.cpp -o testapp
$ ./testapp
-1 : <not found>
0 : a
1 : a
2 : a
3 : a
4 : a
5 : a
6 : a
7 : a
8 : a
9 : a
10 : a
11 : b
12 : b
13 : b
14 : b
15 : b
16 : b
17 : b
18 : b
19 : b
20 : b
21 : c
22 : c
23 : c
24 : c
25 : c
26 : c
27 : c
28 : c
29 : c
30 : c
31 : <not found>
Same idea as #selbie's answer, but using a transparent comparator to avoid wrapping the map in your own class:
#include <compare>
#include <iostream>
#include <map>
#include <string>
template <typename T>
struct Range
{
T begin{}, end{};
friend constexpr auto operator<=>(const Range &, const Range &) = default;
friend constexpr std::weak_ordering operator<=>(const Range &a, T b)
{
if (b < a.begin)
return std::weak_ordering::greater;
else if (b > a.end)
return std::weak_ordering::less;
else
return std::weak_ordering::equivalent;
}
};
int main()
{
std::map<Range<int>, std::string, std::less<>> m = {
{{0, 5}, "A"},
{{6, 10}, "B"},
{{11, 15}, "C"},
};
auto check = [&](int n)
{
auto it = m.find(n);
if (it != m.end())
std::cout << n << " -> " << it->second << '\n';
else
std::cout << n << " not in the map\n";
};
check(0); // A
check(1); // A
check(5); // A
check(6); // B
check(-1); // not in the map
check(16); // not in the map
}
First, we use std::less<> (without the template argument - the transparent version), which makes .find() a template, accepting any type comparable with the key type.
Next we make a range class that overloads comparison operators (using C++20) with itself and with its element type.
You could do the same thing with std::pair and with a custom transparent comparator (add using is_transparent = void;) that compares it with the numbers in the correct way.
Related
I am trying to find a way to count how many elements are equal in 2 different vectors of the same size in c++. The vectors hold structs and i want to compare the equality by a double variable of the struct shown on the example.
And to make it clear. I do NOT want to check if the 2 vectors are equal but only to count how many of their elements are.
The following doesn't work. It gives addresses instead of values. Also If I try to access the dist variable like pointsA[j].dist I get an error.
vector<struct PointWithDistance*> pointsA, pointsB;
//the struct
struct PointWithDistance {
Point *p;
double dist;
};
for (int j = 0; j < k; j++){
if (pointsA[j] == pointsB[j])
equalCount++;
}
vector<struct PointWithDistance*> pointsA, pointsB;
Did you mean to use pointers? If so, you have to do *(points[A]) (and b) because your current comparison compares the pointers, not their content.
Also, does the struct Point have an operator == so comparison on the type can be performed??
Do you want to force same positions? say, a vector {1,2,3} and a vector {2,3,4} by your algorithm will have 0 items equal, do you want that? If not, loop the first vector and use std::find (or std::upper_bound if the vector is sorted) on each element to the second vector.
Some quick code:
template <typename T=int> struct Point
{
T x,y;
bool operator==(const T& t) { return (x == t.x && y == t.y); }
};
std::vector<Point<>> p1 = {1,2,3};
std::vector<Point<>> p2 = {2,3,4};
for(auto& p : p1)
{
if (std::find(p2.begin(),p2.end(),p) != p2.end())
{
// similar++;
}
}
// or
assert(p1.size() == p2.size());
for(size_t i1 = 0 ; i1 < p1.size() ; i1++)
{
if (p1[i1] == p2[i1])
{
// equal++;
}
}
A generic solution to count the number of duplicates in 2 containers could look like this. Using std::transform_reduce to add (std::plus<>{}) the boolean result if an element was found in the container. Note how it can accep two different types of containers as long as their contained type stays the same (e.g. std::vector<int> and std::set<int>). The length of the containers don't have to be equal. There are two SFINAE implementations to differenciate between the case where T is a pointer and where it isn't:
#include <algorithm> //std::find, std::find_if
#include <cstddef> //std::size_t
#include <functional> //std::plus,
#include <iterator> //std::cbegin, std::cend
#include <numeric> //std::transform_reduce
#include <type_traits> //std::enable_if_t, std::is_pointer_v
namespace {
//core implementation for duplicate_count
template<class C, class F>
std::size_t duplicate_count_impl(const C& container, F pred) {
return std::transform_reduce(std::cbegin(container), std::cend(container), std::size_t{}, std::plus<>{}, pred);
}
}
//returns the number of duplicates in two (different) containers.
//overload for containers where T is a pointer type.
template<typename T, template <typename...> class C1, template <typename...> class C2, std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
std::size_t duplicate_count(const C1<T>& a, const C2<T> &b) {
return duplicate_count_impl(b, [&](T ptr_b) -> bool {
return std::find_if(std::cbegin(a), std::cend(a), [&](T ptr_a) -> bool {
return *ptr_a == *ptr_b;
}) != std::cend(a);
});
}
//returns the number of duplicates in two (different) containers.
//overload for containers where T is not a pointer type.
template<typename T, template <typename...> class C1, template <typename...> class C2, std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
std::size_t duplicate_count(const C1<T>& a, const C2<T> &b) {
return duplicate_count_impl(b, [&](T n) -> bool {
return std::find(std::cbegin(a), std::cend(a), n) != std::cend(a);
});
}
#include <iostream>
#include <vector>
#include <list>
//[duplicate_count implementations]
struct Point {
int a, b;
bool operator==(const Point& other) const {
return this->a == a && this->b == other.b;
}
};
int main() {
{
std::list<int> v = { 1, 2, 7, 7 };
std::list<int> u = { 0, 1, 2, 7 };
std::cout << "list<int>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
{
auto[a, b, c, d] = std::make_tuple(0, 1, 2, 3);
std::vector<int*> v = { &b, &c, &d, &d };
std::vector<int*> u = { &a, &b, &c, &d };
std::cout << "vector<int*>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
{
auto[a, b, c, d] = std::make_tuple(
Point{ 0, 0 },
Point{ 1, 1 },
Point{ 2, 2 },
Point{ 4, 4 });
std::vector<Point*> v = { &b, &c, &d, &d };
std::vector<Point*> u = { &a, &b, &c, &d };
std::cout << "vector<Point*>\t number of duplicates: " << duplicate_count(v, u) << '\n';
}
}
list<int> number of duplicates: 3
vector<int*> number of duplicates: 3
vector<Point*> number of duplicates: 3
Your shown solution is good, fast and efficient.
It has some minor problem that can easily be resolved. In your definition vector<struct PointWithDistance*> pointsA, pointsB;, variables pointsA and pointsB are vectors, containing pointer to structs.
With pointsA[n] you will get a pointer to the struct. But you want the struct by itself. So you simply need to dereference the gotten pointer. And since you want to access a member of a struct (usually done with variable.member), you can use (*(pointsA[j])).dist or pointsA[j]->dist.
If the size of your vectors are guaranteed the same, then you simply need to update your code to
vector<struct PointWithDistance*> pointsA, pointsB;
//the struct
struct PointWithDistance {
Point *p;
double dist;
};
for (int j = 0; j < k; j++){
if (pointsA[j]->dist == pointsB[j]->dist)
equalCount++;
}
That is the only thing you were missing.
You can use the std::inner_product algorithm:
#include <iostream>
#include <numeric>
#include <vector>
int main () {
const std::vector a{7, 7, 7, 7};
const std::vector b{7, 6, 7, 7};
const auto equalCount = std::inner_product(
a.begin(), a.end(), b.begin(), 0,
std::plus<>(), std::equal_to<>()
);
std::cout << equalCount << " of the elements are equal.\n";
}
outputs
3 of the elements are equal.
It is a generalization of the standard inner product,
using the functions + (plus) and == (equal_to),
instead of + and *.
Thus it computes
0 + (a[0] == b[0]) + (a[1] == b[1]) + ....
This uses the fact that false/true can be interpreted as 0/1.
Lately I was using boost-range to create ranges over elements satisfying certain criteria. In all cases I'm using the same kind of filtered range all the time, so that I tried to encapsulate this behaviour in an external function.
This was the point where my problems started. Consider the following example.
#include <boost/range/adaptor/filtered.hpp>
#include <iostream>
#include <vector>
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
int main(int argc, const char* argv[])
{
using namespace boost::adaptors;
std::vector<int> input{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto& element : input | filtered([](auto v) {return v % 2 == 0; } ))
{
std::cout << "Element = " << element << std::endl;
}
std::cout << std::endl;
for (auto& element : myFilter(input,4))
{
std::cout << "Element = " << element << std::endl;
}
return 0;
}
The first for-loop behaves as expected printing 4 and 8. The second for-loop however prints just 4. Why is that?
My second idea was to implement a class having a begin() and end() function. This should be a thin wrapper around a range object.
This was the solution, after fiddling out the type of the range iterator.
struct MyFilter {
MyFilter(const std::vector<int>& c, int r) : c(c), r(r), f([&r](auto v) { return v%r == 0; }) {
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator begin() {
return rng.begin();
}
boost::range_detail::filtered_range<std::function<bool(int)>, std::vector<int>>::iterator end() {
return rng.end();
}
std::vector<int> c;
int r;
std::function<bool(int)> f;
boost::range_detail::filtered_range < std::function<bool(int)>, std::vector<int>> rng=c | boost::adaptors::filtered(f);
};
Usage should be something like:
for (auto& element : MyFilter(input, 4)) {
std::cout << "Element = " << element << std::endl;
}
Unfortunately, it prints again just the 4. Whichs is quite strange to me??
Now, I got the solution by myself. I have to remove the "&" in my lambda function to make it work!
In:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([&r](auto v) { return v%r == 0; });
};
It returns another range adaptor while r captured by reference becomes a dangling reference. To fix it capture r by value:
auto myFilter = [](const std::vector<int>& v, int r) {
return v | boost::adaptors::filtered([r](auto v) { return v%r == 0; });
}; ^
+--- capture by value
Is there a way to change how an enum sets the values of its constants? Normally it's incrementing by one but I want to apply an other rule. In PAWN this would work
enum (<<=1) {
a = 1,//0b001
b,//0b010
c//0b100
}
Is there a way to do this in C++?
not automatically, but you can specify manually each value
enum X {
a = 0x01,
b = 0x02,
c = 0x04
};
You could automate this shifting process using templates metaprogramming:
template<int by>
struct OneShlBy {
enum { Value = OneShlBy<by - 1>::Value << 1 };
};
template<>
struct OneShlBy<0> {
enum { Value = 1 };
};
enum X {
a = OneShlBy<0>::Value,
b = OneShlBy<1>::Value,
c = OneShlBy<2>::Value
};
One way, assuming C++11, would be
constexpr int func(int x)
{
return x << 1;
}
enum X
{
a = 1,
b = func(a),
c = func(b)
};
The func() can then be used to implement any relationship you like between consecutive named values.
Before C++11, alternatives might include
enum X
{
a = 1,
b = a << 1,
c = b << 1
};
or (with a macro)
#define func(x) x << 1
enum X
{
a = 1,
b = func(a),
c = func(b)
};
#undef func
One common approach for the particular example you give (enums which are essentially flags within a word) is to use two enums, one for the bit index and a second enum for the actual bit values:
enum {
b_a, // 0
b_b, // 1
b_c, // 2
b_d, // 3
};
enum {
a = 1 << b_a, // 1
b = 1 << b_b, // 2
c = 1 << b_c // 4
d = 1 << b_d // 8
};
It's not ideal, I know, but it avoids explicit literal constants. Some of the nastiness can be hidden with macros, which you may or may not consider a good thing to do.
What you were doing works perfectly fine:
#include <iostream>
enum x {
a = 0b001
, b = 0b010
, c = 0b100
};
int main()
{
std::cout << x::a << '\n';
std::cout << x::b << '\n';
std::cout << x::c << '\n';
}
Note: this is only standard in C++14 where binary literals are introduced.
Otherwise you can use the shift operator to specify the bit position:
enum x {
a = 1 << 0
, b = 1 << 1
, c = 1 << 2
};
Another possibility if you are looking to save some maintenance work is defining each enum in terms of previously defined enums:
enum x {
a = 1
, b = a << 1
, c = b << 1
, d = c << 1
, e = d << 1
};
This means you can add an element in the middle relatively painlessly:
enum x {
a = 1
, b = a << 1
, z = b << 1 // new element added
, c = z << 1 // only need to adjust one other
, d = c << 1
, e = d << 1
};
Stealing the great idea of Rob, there's a way to specify how to increment the value of an enum, this it isn't very automatic though, but as long as your enum have always the same fields you can do something like this:
template <typename T, T(*INC)(const T), T start = {}>
struct managed_enum
{
enum
{
a = start,
b = INC(a),
c = INC(b),
};
};
With this template you could do the following:
template<typename T>
constexpr T shift_left(const T value) { return value << 1; }
using shifted = managed_enum<int, shift_left<int>, 1>;
Which results on the enum you're looking for (see here). Well, shifted isnt an enum but a struct, hope it doesn't matters.
Defining different increment functions, you can have different families of enums automatically increased, as long as all of them have only the three values of managed_enum (a, b, c):
template<typename T>
constexpr T shift_left(const T value) { return value << 1; }
template<typename T>
constexpr T increment(const T value) { return value + 1; }
template<typename T>
constexpr T bicrement(const T value) { return value + 2; }
using shifted = managed_enum<int, shift_left<int>, 1>;
using increment = managed_enum<int, increment<int>>;
using bicrement = managed_enum<int, bicrement<int>>;
I have a vector of struct member like below:
struct pnt
{
bool has;
int num;
};
std::vector<pnt> myvector;
let have a sample vector like:
myvector (num): 3 4 4 3 5 5 7 8 9 10 10
myvector (has): 1 1 0 1 0 1 0 0 0 1 0
What I want to do is to find duplicated members (in terms of having same int num) and remove the one with false bool member.
so that my vector become like this:
myvector (num): 3 4 3 5 7 8 9 10
myvector (has): 1 1 1 1 0 0 0 1
to do so, I write following function:
void removeDuplicatedPnt(pnt_vec& myvector)
{
std::vector<pnt>::iterator pnt_iter;
for( pnt_iter = myvector.begin(); pnt_iter != myvector.end(); ++pnt_iter)
{
if(pnt_iter->has)
{
if(pnt_iter->num == (pnt_iter+1)->num)
{
myvector.erase(pnt_iter+1);
}
if(pnt_iter == myvector.begin())
{
continue;
}
if(pnt_iter->num == (pnt_iter-1)->num)
{
myvector.erase(pnt_iter-1);
pnt_iter++;
}
}
}
}
I could also do it by sequential checking of members. but the real vector could be very long. so that is why first I went to find the member with true boolean then I checked the the next and previous member. The question is that how I can modify above code in terms of efficiency and robustness.
NOTE: I can only use C++03 (not C++11). I can also use boos (version 1.53), so feel free if think there is any useful function there. :)
You can use this algorithm:
Collect all nums where has is true in a set<int>
Go through your vector<pnt> again, and remove all entries where has is false and num is present in the set<int>
Here is a sample implementation:
struct filter {
set<int> seen;
bool operator()(const pnt& p) {
return !p.has && (seen.find(p.num) != seen.end());
}
};
...
filter f;
for (vector<pnt>::const_iterator i = v.begin() ; i != v.end() ; i++) {
if (i->has) {
f.seen.insert(i->num);
}
}
v.erase(remove_if(v.begin(), v.end(), f), v.end());
Demo.
You can use std::sort and std::unique with a custom comparison predicate:
[](const pnt& a, const pnt& b) { return a.num < b.num; }
Here's a convenient way to demo it using Boost Range to reduce on typing:
Update C++03 version Live On Coliru
#include <boost/range.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>
using namespace boost;
struct pnt {
int num;
bool has;
pnt(int num = 0, bool has = false) : num(num), has(has) {}
friend bool operator<(pnt const& a, pnt const& b) { return a.num<b.num; }
friend bool operator==(pnt const& a, pnt const& b) { return a.num==b.num; }
};
int main() {
std::vector<pnt> v { {10,0 },{10,1 },{9,0 },{8,0 },{7,0 },{5,1 },{5,0 },{3,1 },{4,0 },{4,1 },{3,1 } };
for (pnt p : boost::unique(boost::sort(v)))
std::cout << "{ num:" << p.num << ", has:" << p.has << "}\n";
}
This actually has a few subtler points that make it possible to e.g. do
it = std::find(v.begin(), v.end(), 3); // find a `pnt` with `num==3`
But that's only tangentially related
Is it possible/achievable to negate a boost filtered adaptor, e.g.
std::vector<int> v = {1, 2, 3, 4, 5};
for(auto i : v | !filtered(is_even))
std::cout << i << std::endl; // prints 1,3,5
instead of doing the negation inside the lambda expression?
Motivation: I work a lot with filtered and lambda functions, however when I use a filter more than once I usually refactor it into a custom filter, e.g.
for(auto i : v | even) // note: my filters are more complex than even.
std::cout << i << std::endl; // prints 2,4
Right now when I need the negation I am building a custom filter for them, e.g.
for(auto i : v | not_even)
std::cout << i << std::endl; // prints 1,2,3
but I would find it nicer to just be able to negate a filter, e.g.
for(auto i : v | !even)
std::cout << i << std::endl; // prints 1,2,3
Here's what I came up with on short notice:
#include <boost/range/adaptors.hpp>
#include <boost/functional.hpp>
#include <iostream>
namespace boost {
namespace range_detail {
template <typename T>
auto operator!(filter_holder<T> const& f) -> decltype(adaptors::filtered(boost::not1(f.val)))
{
return adaptors::filtered(boost::not1(f.val));
}
}
}
int main()
{
using namespace boost::adaptors;
int const v[] = { 1, 2, 3, 4 };
std::function<bool(int)> ll = [](int i){return 0 == (i%2);}; // WORKS
// bool(*ll)(int) = [](int i){return 0 == (i%2);}; // WORKS
// auto ll = [](int i){return 0 == (i%2);}; // not yet
auto even = filtered(ll);
for (auto i : v | !even)
{
std::cout << i << '\n';
}
}
See it live on liveworkspace.org
Note that it currently handles predicates of the form function pointer and std::function<...>, but not naked lambdas yet (on GCC 4.7.2)
This doesn't excactly answer the question, because it doesn't negate the filter, but only the predicate. I'm still posting this, because searching for the solution brought this question up as the first result.
Compared to the other answer, this has the advantage that we don't need to add custom code to namespace boost::range_detail.
C++17 Solution
The function std::not_fn can be used to create a negated predicate.
#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>
struct is_even
{
bool operator()( int x ) const { return x % 2 == 0; }
};
int main()
{
using namespace boost::adaptors;
int const v[] = { 1, 2, 3, 4 };
for( auto i : v | filtered( std::not_fn( is_even{} ) ) )
{
std::cout << i << ' ';
}
}
Live Demo
C++11, C++14 Solution
The function std::not1 can be used to create a negated predicate. It has the additional requirement, that the predicate must define a member type, argument_type which has the same type as the predicates operator() parameter.
#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>
struct is_even
{
using argument_type = int;
bool operator()( int x ) const { return x % 2 == 0; }
};
int main()
{
using namespace boost::adaptors;
int const v[] = { 1, 2, 3, 4 };
for( auto i : v | filtered( std::not1( is_even{} ) ) )
{
std::cout << i << ' ';
}
}
Live Demo