Consider the following code:
#include <boost/range.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <list>
struct TestData {
TestData() : m_strMem01("test"), m_intMem02(42), m_boolMem03(true) {}
std::string m_strMem01;
int m_intMem02;
bool m_boolMem03;
};
struct IntComp {
bool operator()(const TestData &s, int i) { return s.m_intMem02 < i; }
bool operator()(int i, const TestData &s) { return i < s.m_intMem02; }
bool operator()(const TestData &i, const TestData &s) {
return i.m_intMem02 < s.m_intMem02;
}
};
struct StrComp {
bool operator()(const TestData &s, const std::string &str) {
return s.m_strMem01 < str;
}
bool operator()(const std::string &str, const TestData &s) {
return str < s.m_strMem01;
}
bool operator()(const TestData &i, const TestData &s) {
return i.m_strMem01 < s.m_strMem01;
}
};
typedef boost::any_range<TestData, boost::forward_traversal_tag,
const TestData &, std::ptrdiff_t> TestRange;
std::vector<TestData> vecData(10);
std::list<TestData> listData(20);
TestRange foo() {
TestRange retVal;
auto tmp1 = std::equal_range(vecData.cbegin(), vecData.cend(), 42, IntComp());
retVal = boost::join(retVal, tmp1);
auto tmp2 =
std::equal_range(listData.cbegin(), listData.cend(), "test", StrComp());
retVal = boost::join(retVal, tmp2);
return retVal;
}
int main(int argc, char *argv[]) {
auto res = foo();
for (auto a : res) {
std::cout << a.m_strMem01 << std::endl;
}
//std::cout << res[4].m_intMem02 << std::endl;
}
If you uncomment the last line the code fails since distance_to not implemented for any_forward_iterator_interface. I'm not sure what exactly I'm missing here, like implementing operator[] or distance_to but for what? My own version traversal tag? And why it doesn't work in the first place?
Coliru version
I would say the answer depends on your performance needs and your laziness when it comes to implementing a new iterator abstraction. The core reason for your [] operator not working is the fact that std::list<...> does not provide a random access traversal iterator. If you would have chosen a container that provides such an iterator. You any_range<...> could have taken the random_access_traversal_tag and everything would be fine.
I think it's fair to say that it is not such a big deal to implement a random access iterator on top of a list by simply encapsulating the current index and count forward and backward within the list whenever a specific position is meant to be accessed, but it's clearly against the nature of the list performance-wise.
Is there a good reason to hold one of the collection in a list ?
Is there a good reason to access the resulting any_range by random ?
Is it worth the effort to provide a inefficient random access interface for std::list ?
Of course any_iterator (which underlies the any_range implementation) doesn't gratuitously emulate RandomAccess iterators for any odd iterator you pass.
If you want that, just make an iterator adaptor that does this (making it very slow to random access elements in a list - so don't do this).
Related
I have a unordered_map<int,int> type variable. I want to sort the map according to its value. I was wondering if I can use callback in sort function, so that it returns sorted map according to it's value.
unordered_map<int,int> p;
for(int i=0;i<nums.size();i++){
p[nums[i]]++;
}
sort(p.begin(),p.end(),callback);
You can't sort a unordered_map in-place. You need to build a sortable alternate container, dump the content there (or bring appropriate material necessary to reference the pairs in the original map), then sort away. There are a multitude of ways to accomplish this. A few examples are shown below.
Value Copy and Sort
The following generates one-hundred random draws in the inclusive domain 1..10, then prints the frequency table after sorting based on frequency (value) descending (the unspoken task I suspect to be driving all of this to begin with):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<std::pair<int,int>> v { p.begin(), p.end() };
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2.second < pr1.second; });
for (auto const& pr : v)
std::cout << pr.first << ':' << pr.second << '\n';
}
Output (varies)
3:14
4:13
7:13
9:11
1:11
2:10
10:7
6:7
8:7
5:7
Pointers to Pairs
An alternate mechanism using constant pointers to the original map pairs is also possible (and probably preferable if the mapped content is either too expensive or outright impossible to make copies of):
#include <iostream>
#include <algorithm>
#include <vector>
#include <unordered_map>
#include <random>
int main()
{
std::mt19937 rng{ std::random_device{}() };
std::uniform_int_distribution<> dist(1, 10);
std::unordered_map<int, int> p;
for (int i = 0; i < 100; ++p[dist(rng)], ++i);
std::vector<const decltype(p)::value_type*> v;
for (auto const& pr : p)
v.emplace_back(&pr);
std::sort(v.begin(), v.end(),
[](auto const& pr1, auto const& pr2)
{ return pr2->second < pr1->second; });
for (auto const& pr : v)
std::cout << pr->first << ':' << pr->second << '\n';
}
Output (varies)
2:16
1:14
3:11
7:11
4:9
10:9
5:8
8:8
9:8
6:6
Caveats apply to this approach. Those pointers to pairs are only as good as the day they were cashed. E.g., modify the original map by shoving new keys in, or trimming existing keys out, and the entire pointer bed is disavowed. That obviously includes outright destroying the original map. Buyer beware.
You are addressing 2 problems here:
Sorting a std::map or std::unordered_map according to its value
Using a kind of "compare callback" function for std::sort
Let us first tackle the point 2. If we read the description of std::sort in the CPP Reference here, then we see that can use a "comparison function object" as the 3rd parameter. This can be implemented with a Lambda, a functor or with adding a comparison operator to the object that will be sorted.
Let us assume we have a struct in a std::vector that we want to sort.
struct IntVal {
int val{};
};
std::vector<IntVal> intVals{};
If we want to sort this with a Lambda, then:
std::sort(intVals.begin(), intVals.end(), [](const IntVal& iv1, const IntVal& iv2) { return iv1.val < iv2.val; });
Or, you could add a comparison operator to your class/struct:
struct IntVal{
int val{};
bool operator < (const IntVal& other) const { return val < other.val; }
};
Or you can make your class a Functor, by adding a callable operator () to it.
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), IntVal());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Or, if you cannot modify the class, the create an external functor:
#include <iostream>
#include <vector>
#include <algorithm>
struct IntVal{
int val{};
};
// Functor
struct Comp {
bool operator () (const IntVal& iv1, const IntVal& iv2) const { return iv1.val < iv2.val; }
};
std::vector<IntVal> intVals{ {3},{2},{1} };
int main() {
std::sort(intVals.begin(), intVals.end(), Comp());
for (const IntVal& iv : intVals)
std::cout << iv.val << '\n';
}
Coming to the next topic.
You want to sort the data, stored in the std::unordered_map by its "value"-type.
This is not possible in place, because the nature of the maps is that they are already sorted. You cannot resort them. This would violate their internal behaviour.
The second problem is often that there key is unique. And sorting could also probaly destroy this strict requirement.
The solution is to use a second container. With you above mentioned "Sort" parameter. Copy the data to a second container and sort this.
We can use a std::vector and its range constructor to copy the data.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
// Functor
struct Comp {
bool operator () (const std::pair<int,int>& p1, const std::pair<int, int>& p2) { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; }
};
int main() {
std::unordered_map<int, int> test{ {1,4},{2,3},{3,2},{4,1} };
std::vector<std::pair<int, int>> data(test.begin(), test.end());
std::sort(data.begin(), data.end(), Comp());
for (const auto[i1,i2] : data)
std::cout << i1 << ' ' << i2 << '\n';
}
Or, last but not least, and the final solution. We can use a 2nd container that will be sorted by definition, like a std::multiset
This will create a piece of code that is really easy to understand.
Please see:
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <utility>
#include <set>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<int, int>;
// The map
using Map = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Sorter = std::multiset<Pair, Comp>;
int main() {
Map test{ {1,4},{2,3},{3,2},{4,1} };
Sorter sorter(test.begin(), test.end());
for (const auto[i1,i2] : sorter)
std::cout << i1 << ' ' << i2 << '\n';
}
And since what you really want to do is counting, please see an example for this as well. Here we will count as an example, the letters in a string:
#include <iostream>
#include <string>
#include <utility>
#include <set>
#include <unordered_map>
#include <type_traits>
#include <cctype>
// ------------------------------------------------------------
// Create aliases. Save typing work and make code more readable
using Pair = std::pair<char, unsigned int>;
// Standard approach for counter
using Counter = std::unordered_map<Pair::first_type, Pair::second_type>;
// Sorted values will be stored in a multiset
struct Comp { bool operator ()(const Pair& p1, const Pair& p2) const { return (p1.second == p2.second) ? p1.first<p2.first : p1.second>p2.second; } };
using Rank = std::multiset<Pair, Comp>;
// ------------------------------------------------------------
// --------------------------------------------------------------------------------------
// Compact function to calculate the frequency of charcters and then get their rank
Rank getRank(std::string& text) {
// Definition of our counter
Counter counter{};
// Iterate over all charcters in text and count their frequency
for (const char c : text) if (std::isalpha(c)) counter[char(std::tolower(c))]++;
// Return ranks,sorted by frequency and then sorted by character
return { counter.begin(), counter.end() };
}
// --------------------------------------------------------------------------------------
// Test, driver code
int main() {
// Get a string from the user
if (std::string text{}; std::getline(std::cin, text))
// Calculate rank and show result
for (const auto& [letter, count] : getRank(text))
std::cout << letter << " = " << count << '\n';
}
I have an object with functions for getting the begin and end iterators:
const_iterator err_begin() const
const_iterator err_end() const
Because they are not named begin and end, I cannot pass my object directly to functions in range-v3.
Is there a simple wrapper I can use to make this object work with the range-v3 library?
For example:
auto hasErrors = !empty(something(x.err_begin(), x.err_end()));
Sounds like you're looking for iterator_range:
auto hasErrors = !empty(ranges::make_iterator_range(x.err_begin(), x.err_end()));
You clarified that the class in question is part of a library that you cannot change. Fine. Create a facade class:
class FacadeClass {
const RealClassWithErrBeginEnd &r;
public:
FacadeClass(const RealClassWithErrBeginEnd &r) : r(r) {}
auto begin() const { return r.err_begin(); }
auto end() const { return r.err_end(); }
};
This should be good enough to fool most code that expects a container. In the worst case, you might need to provide additional typedefs in the facade, i.e. value_type, etc...
boost::make_iterator_range will do the right thing. Now add a little ADL and we find that one free function solves all our problems:
#include <vector>
#include <iostream>
#include <string>
#include <boost/range.hpp>
// simulate the library class
struct X
{
auto err_begin() const { return errors.begin(); }
auto err_end() const { return errors.end(); }
std::vector<std::string> errors;
};
// provide a generator to build an iterator range
auto errors(const X& x)
{
return boost::make_iterator_range(x.err_begin(), x.err_end());
}
// do things with the iterator_range
int main()
{
X x;
for (const auto& err : errors(x))
{
std::cout << err << std::endl;
}
std::cout << empty(errors(x)) << std::endl;
}
I'm practicing the question of making a hash table using only arrays.
I have a Hash_Entry myMap[128][128].
Can I initiate all values to NULL or 0 even though it's type hash_entry?
and if I can't my issue is checking when the second-dimension array is full.
I thought of doing
is_Full(arr){
if(arr.length()-1.key!=0 && arr.length()-1.value!=0){
return false;
}
else return true;
}
Is that I good implementation?
OR I thought of having a second array that increments a count for each bucket then testing if that count is equal to the bucket size
This code is certainly not C++. Assuming what you're trying to accomplish actually IS in c++, you can write a simple template to check if a member of std::array is equal to a member-provided emptyValue.
#include <array>
#include <iostream>
#include <algorithm>
template<typename T, size_t N, T emptyVal = NULL>
bool arrayIsFull(const std::array<T, N> &arr) {
return !std::any_of(
std::cbegin(arr), // | const iterators
std::cend(arr), // |__ c++11 and up
[](const T &val) { return val == emptyVal; } // simple lambda to check if any member is equal to the expected empty value.
);
};
Checking if they are all full is trivial.
static std::array<std::array<int, 10>, 10> twoDimArr;
void checkIfAllFull(void) {
bool allFull = std::all_of(
std::cbegin(twoDimArr),
std::cend(twoDimArr),
[](const auto &oneDimArr) {return arrayIsFull(oneDimArr); }
);
std::cout << (allFull ? "Filled up!\n" : "There's still some room!\n");
}
int main(int argc, char **argv) {
for (auto it = std::begin(twoDimArr); it != std::end(twoDimArr); ++it) {
std::fill(std::begin(*it), std::end(*it), 1);
}
checkIfAllFull();
twoDimArr.at(3).at(7) = 0;
checkIfAllFull();
std::cin.get();
}
I'm not sure what a Hash_Entry is, but you can easily provide your own type and empty value via templates.
How to store elements in set in insertion order.
for example.
set<string>myset;
myset.insert("stack");
myset.insert("overflow");
If you print, the output is
overflow
stack
needed output :
stack
overflow
One way is to use two containers, a std::deque to store the elements in insertion order, and another std::set to make sure there are no duplicates.
When inserting an element, check if it's in the set first, if yes, throw it out; if it's not there, insert it both in the deque and the set.
One common scenario is to insert all elements first, then process(no more inserting), if this is the case, the set can be freed after the insertion process.
A set is the wrong container for keeping insertion order, it will sort its element according to the sorting criterion and forget the insertion order. You have to use a sequenced container like vector, deque or list for that. If you additionally need the associative access set provides you would have to store your elements in multiple containers simultaneously or use a non-STL container like boost::multi_index which can maintain multiple element orders at the same time.
PS: If you sort the elements before inserting them in a set, the set will keep them in insertion order but I think that will not address your problem.
If you don't need any order besides the insertion order, you could also store the insert number in the stored element and make that the sorting criterion. However, why one would use a set in this case at all escapes me. ;)
Here's how I do it:
template <class T>
class VectorSet
{
public:
using iterator = typename vector<T>::iterator;
using const_iterator = typename vector<T>::const_iterator;
iterator begin() { return theVector.begin(); }
iterator end() { return theVector.end(); }
const_iterator begin() const { return theVector.begin(); }
const_iterator end() const { return theVector.end(); }
const T& front() const { return theVector.front(); }
const T& back() const { return theVector.back(); }
void insert(const T& item) { if (theSet.insert(item).second) theVector.push_back(item); }
size_t count(const T& item) const { return theSet.count(item); }
bool empty() const { return theSet.empty(); }
size_t size() const { return theSet.size(); }
private:
vector<T> theVector;
set<T> theSet;
};
Of course, new forwarding functions can be added as needed, and can be forwarded to whichever of the two data structures implements them most efficiently. If you are going to make heavy use of STL algorithms on this (I haven't needed to so far) you may also want to define member types that the STL expects to find, like value_type and so forth.
If you can use Boost, a very straightforward solution is to use the header-only library Boost.Bimap (bidirectional maps).
Consider the following sample program that will display your dummy entries in insertion order (try out here):
#include <iostream>
#include <string>
#include <type_traits>
#include <boost/bimap.hpp>
using namespace std::string_literals;
template <typename T>
void insertCallOrdered(boost::bimap<T, size_t>& mymap, const T& element) {
// We use size() as index, therefore indexing with 0, 1, ...
// as we add elements to the bimap.
mymap.insert({ element, mymap.size() });
}
int main() {
boost::bimap<std::string, size_t> mymap;
insertCallOrdered(mymap, "stack"s);
insertCallOrdered(mymap, "overflow"s);
// Iterate over right map view (integers) in sorted order
for (const auto& rit : mymap.right) {
std::cout << rit.first << " -> " << rit.second << std::endl;
}
}
I'm just wondering why nobody has suggested using such a nice library as Boost MultiIndex. Here's an example how to do that:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <iostream>
template<typename T>
using my_set = boost::multi_index_container<
T,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<>,
boost::multi_index::ordered_unique<boost::multi_index::identity<T>>
>
>;
int main() {
my_set<int> set;
set.push_back(10);
set.push_back(20);
set.push_back(3);
set.push_back(11);
set.push_back(1);
// Prints elements of the set in order of insertion.
const auto &index = set.get<0>();
for (const auto &item : index) {
std::cout << item << " ";
}
// Prints elements of the set in order of value.
std::cout << "\n";
const auto &ordered_index = set.get<1>();
for (const auto &item : ordered_index) {
std::cout << item << " ";
}
}
what you need is this, very simple and a standard library. Example online compiler link: http://cpp.sh/7hsxo
#include <iostream>
#include <string>
#include <unordered_set>
static std::unordered_set<std::string> myset;
int main()
{
myset.insert("blah");
myset.insert("blah2");
myset.insert("blah3");
int count = 0;
for ( auto local_it = myset.begin(); local_it!= myset.end(); ++local_it ) {
printf("index: [%d]: %s\n", count, (*local_it).c_str());
count++;
}
printf("\n");
for ( unsigned i = 0; i < myset.bucket_count(); ++i) {
for ( auto local_it = myset.begin(i); local_it!= myset.end(i); ++local_it )
printf("bucket: [%d]: %s\n", i, (*local_it).c_str());
}
}
I am attempting to optimize a std::vector "search " - index based iterating through a vector and returning and element that matches a "search" criteria
struct myObj {
int id;
char* value;
};
std::vector<myObj> myObjList;
create a few thousand entries with unique id's and values and push them to the vector myObjList.
What is the most efficient way to retrieve myObj that matches the id.
Currently I am index iterating like:
for(int i = 0; i < myObjList.size(); i++){
if(myObjList.at(i).id == searchCriteria){
return myObjList.at(i);
}
}
Note: searchCriteria = int. All the elements have unique id's.
The above does the job, but probably not the most efficient way.
The C++ standard library has some abstract algorithms, which give C++ a kind of functional flavour, as I call it, which lets you concentrate more on the criteria of your search than on how you implement the search itself. This applies to a lot of other algorithms.
The algorithm you are looking for is std::find_if, a simple linear search through an iterator range.
In C++11, you can use a lambda to express your criteria:
std::find_if(myObjList.begin(), myObjList.end(), [&](const myObj & o) {
return o.id == searchCriteria;
});
When not having C++11 available, you have to provide a predicate (function object (=functor) or function pointer) which returns true if the provided instance is the one you are looking for. Functors have the advantage that they can be parameterized, in your case you want to parameterize the functor with the ID you are looking for.
template<class TargetClass>
class HasId {
int _id;
public:
HasId(int id) : _id(id) {}
bool operator()(const TargetClass & o) const {
return o.id == _id;
}
}
std::find_if(myObjList.begin(), myObjList.end(), HasId<myObj>(searchCriteria));
This method returns an iterator pointing to the first element found which matches your criteria. If there is no such element, the end iterator is returned (which points past the end of the vector, not to the last element). So your function could look like this:
vector<myObj>::iterator it = std::find_if(...);
if(it == myObjList.end())
// handle error in any way
else
return *it;
Using std::find_if.
There's an example on the referenced page.
Here's a working example that more precisely fits your question:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct myObj
{
int id;
char* value;
myObj(int id_) : id(id_), value(0) {}
};
struct obj_finder
{
obj_finder(int key) : key_(key)
{}
bool operator()(const myObj& o) const
{
return key_ == o.id;
}
const int key_;
};
int main () {
vector<myObj> myvector;
vector<myObj>::iterator it;
myvector.push_back(myObj(30));
myvector.push_back(myObj(50));
myvector.push_back(myObj(100));
myvector.push_back(myObj(32));
it = find_if (myvector.begin(), myvector.end(), obj_finder(100));
cout << "I found " << it->id << endl;
return 0;
}
And, if you have C++11 available, you can make this even more concise using a lambda:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct myObj
{
int id;
char* value;
myObj(int id_) : id(id_), value(0) {}
};
int main ()
{
vector<myObj> myvector;
vector<myObj>::iterator it;
myvector.push_back(myObj(30));
myvector.push_back(myObj(50));
myvector.push_back(myObj(100));
myvector.push_back(myObj(32));
int key = 100;
it = find_if (myvector.begin(), myvector.end(), [key] (const myObj& o) -> bool {return o.id == key;});
cout << "I found " << it->id << endl;
return 0;
}
This isn't really an answer to your question. The other people who answered gave pretty good answers, so I have nothing to add to them.
I would like to say though that your code is not very idiomatic C++. Really idiomatic C++ would, of course, use ::std::find_if. But even if you didn't have ::std::find_if your code is still not idiomatic. I'll provide two re-writes. One a C++11 re-write, and the second a C++03 re-write.
First, C++11:
for (auto &i: myObjList){
if(i.id == searchCriteria){
return i;
}
}
Second, C++03:
for (::std::vector<myObj>::iterator i = myObjList.begin(); i != myObjList.end(); ++i){
if(i->id == searchCriteria){
return *i;
}
}
The standard way of going through any sort of C++ container is to use an iterator. It's nice that vectors can be indexed by integer. But if you rely on that behavior unnecessarily you make it harder on yourself if you should change data structures later.
If the ids are sorted you may perform binary search(there is also a function binary_search in stl). If they are not nothing will perform better, but still you may write your code in a shorter way using stl(use find_if).