I need to get the pairs of the map sorted by its values, i wonder if it is posible without an temporal declaration.
I know i can sort it if i make another map with the keys and values swaped, but i am searching for a better solution.
I can't sort the elements afterwards because i only need extract the chars and put them on an array for example.
std::map<char,int> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
for(/* code here */){
std::cout << /* code here */ << std::endl;
}
Desired outout:
J 5
A 4
G 3
V 2
N 1
This cannot be done with std::map.
This template has an optional template argument which allows for a custom sorting, but this sorting can only be done on the map's key:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare.
This choice has been done as to not impose the map's value to be an orderable type.
As an alternative, you can use type std::vector<std::pair<char, int>> in combination with std::sort.
#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::pair<char,int>> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
std::sort(begin(list), end(list), [](auto lhs, auto rhs) {
return lhs.second > rhs.second ? true : ( rhs.first > rhs.first );
});
for(auto const& pair : list) {
std::cout << pair.first << ", " << pair.second << '\n';
}
}
J, 5
A, 4
G, 3
V, 2
N, 1
Live demo
The most appropriate solution depends on HOW are you going to use that list. Is it created once and queried once? Then anything will do: you can just sort the output.
However, if this is a "live" list that is constantly updated, and you need to query it frequently, I would suggest to keep another map of values to vector of keys. Then you would be able to get your result instantly. At a cost
of more expensive insertion, of course.
Related
I want to change the std::map (it's hash function) so when iterated to return pairs in the way they are inserted. I have tried with unordered map, but with no success. So I guess I have to create a hash function that is incrementing and returns bigger value every time. How can I do that? I'm not working with big data, so performance is not a problem.
It sounds like you want an ordered map, a hashmap which allows O(1) lookups but remembers the order of insertion (for iteration order). Luckily, an efficient implementation exists in C++:
https://github.com/Tessil/ordered-map
An example from the README is:
#include <iostream>
#include <tsl/ordered_map.h>
int main()
{
tsl::ordered_map<char, int> map = {{'d', 1}, {'a', 2}, {'g', 3}};
map.insert({'b', 4});
map['h'] = 5;
map['e'] = 6;
map.erase('a');
// {d, 1} {g, 3} {b, 4} {h, 5} {e, 6}
for(const auto& key_value : map) {
std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
}
return 0;
}
You may also implement it like Python's ordered map, which uses a linked list to track insertion order. An advantage is more efficient deletions (Tessil's ordered-map has O(n) deletions, while a linked list/map would be average O(1)). Assuming the map would normally store a key/value type of type Key, T, you would store T in the linked list, and store an iterator as the internal unordered_map's value type. You would then wrap the class and any iterators to ensure it acts from the outside like a normal hashmap.
The basic outline would look something like the following (the rest is for you to implement, if O(n) deletions are unacceptable). Lists are used since they do not invalidate iterators when an element is removed, and the map is used to find the correct list iterator for deletions by key. Satisfies all the big O issues, albeit with some overhead.
#include <list>
#include <unordered_map>
template <
typename Key,
typename T,
typename Hash = std::hash<Key>,
typename KeyEqual = std::equal_to<Key>,
typename Allocator = allocator<std::pair<Key, T>>
>
class ordered_map
{
public:
using key_type = Key;
using mapped_type = T;
// ....
private:
using list_type = list<T, typename std::allocator_traits<Allocator>::template rebind_alloc<T>>;
using list_iterator = typename list_type::iterator;
using map_type = std::unordered_map<
Key, list_iterator, Hasah, KeyEqual,
typename std::allocator_traits<Allocator>::template rebind_alloc<std::pair<Key, list_iterator>>
>;
list_type list_;
map_type map_;
};
I think it would be better to use:
std::vector<std::pair<key,value> myValues.
For insertion you would use code like this
myValues.emplace_back(std::make_pair<key,value>(someKey, someValue));
Preferably you will do this in a loop. Then when you want to get items in order they were inserted you simply loop on index For example using for-ranged based loop to output elements in order they were insterted:
for(auto & pair : myValues)
{
std::cout << "Key: " << pair.first << " Value: " << pair.second << std::endl;
}
Doing this on unordered_map or regular map container would be bad use case for such container.
I can sort an unordered map by value in descending order using this answer.
However, using a set for the same job fails:
#include <set>
#include <functional>
#include <iostream>
using namespace std;
typedef pair<string, int> Pair;
typedef function<bool(Pair, Pair)> Comparator;
Comparator DescendingSortComparator = [](Pair pair1, Pair pair2) {
return pair1.second > pair2.second;
};
void SortHashTableByValueDescending(unordered_map<string, int> hashTable) {
set<Pair, Comparator> orderedSet(hashTable.begin(), hashTable.end(), DescendingSortComparator);
for (auto element : orderedSet)
cout << element.first << ": " << element.second << endl;
}
Running with the following test:
void Test_SortMap()
{
unordered_map<string, int> CountTable;
CountTable["word"] = 1;
CountTable["spark"] = 15;
CountTable["the"] = 2;
CountTable["mail"] = 3;
CountTable["info"] = 3;
CountTable["sandwich"] = 15;
SortHashTableByValueDescending(CountTable);
}
yiels the following output:
spark: 15
info: 3
the: 2
word: 1
Can anyone please tell me why set (probably) overwrites pairs with same value? The keys for such pairs are distinct anyways.
See the definition of Compare function of std::set.
Everywhere the standard library uses the Compare concept, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent if neither compares less than the other: !comp(a, b) && !comp(b, a).
This means that equal numbers will considered equivalent and not copied into your orderedSet
Use
Comparator DescendingSortComparator = [](Pair pair1, Pair pair2) {
if (pair1.second == pair2.second)
return pair1.first > pair2.first;
else return pair1.second > pair2.second;
};
if you want to keep them
From the cppreference.com:
std::set is an associative container that contains a sorted set of
unique objects of type Key.
According to your Comparator only a single std::pair with a fixed second element can be stored in the set.
This is a conceptual question, so I am not providing the "working code" for this reason.
Imagine one has two std::vector of different types and different number of entities, just for example:
vector <int> A;
vector <string> B;
One has a set of rules following which one can associate any members of A with some(or none) of the members of B.
Is there a way to "store" this connection?
I was thinking that one of the ways to do so is having a vector <map <int, vector <string> > > or vector <map <int, vector <string*> > >, but this solutions seems to me unreliable (if A contains two same numbers for example) and I assume there are much more elegant solutions somewhere there.
You could implement some database techniques: indices. Place your data into a single vector then create std::map for each way you want to index your data or relate the data.
Rather than 2 vectors, make one vector of structures:
struct Datum
{
int value;
string text;
};
// The database
std::vector<Datum> database;
// An index table by integer
std::map<int, // Key
unsigned int vector_index> index_by_value;
// An index table, by text
std::map<std::string, // Key
unsigned int index_into_vector> index_by text;
The index tables give you a quick method to find things in the database, without having to sort the database.
A std::multiset of std::pairs would be able to map multiple int*s to zero or more std::string*s:
std::multiset < std::pair<int*, std::vector<std::string*>>> map_A_to_B;
Example:
#include <set>
#include <vector>
#include <string>
#include <utility>
#include <iostream>
int main()
{
std::vector<int> A{3,3,1,5};
std::vector<std::string> B{"three a", "three b", "one", "five", "unknown"};
std::multiset < std::pair<int*, std::vector<std::string*>>> map_A_to_B{
{&A[0],{&B[0],&B[1]}},
{&A[1],{&B[0],&B[1],&B[4]}},
{&A[2],{&B[2]}},
{&A[3],{&B[3]}},
};
for(auto e : map_A_to_B) {
for(auto s : e.second) {
std::cout << *e.first << " linked to " << *s << '\n';
}
std::cout << "------------------------------\n";
}
}
produces:
3 linked to three a
3 linked to three b
------------------------------
3 linked to three a
3 linked to three b
3 linked to unknown
------------------------------
1 linked to one
------------------------------
5 linked to five
------------------------------
Based on your comment, it seems like you want an actual mapping (as in math, from a set A to a set B) that is general (not one-to-one or onto). First you have to conceptually understand what you want. First, you want a mapping between a class A (say int in your example) to B (string). Let's template this:
template <class From, class To>
bool isMapped(From A,To B) {
return (//Code representing mapping,say check if A=int->char is in B=string)
}
Now the mapping of a From value to a To vector is (in math terms) the range in "To" which is reachable (isMapped) form this value:
template<class From, class To>
List<To>& getRange(From value, To range) {
List<To> result();
for (const auto& toValue : range) {
if(isMapped(value,toValue)
result.push_back(toValue);
return result;
This will return the range the From value is mapped to in the To vector, with duplicates if they appear more than once in the range. Another option (maybe better) would be to iterate over indices instead of values in the range, and return a Boolean vector of the length of range with true in the indices where From is mapped to.
Similarly you would need to define the opposite mapping. Probably you couldn't make this completely general, and maybe even templates won't fit this simply - you would need to give more specifics.
So concluding, the mapping from A to B would be a vector of length of vector A (the domain) of vectors of length B (domain) with True/False in the relevant indices.
There are of course, more possibilities.
You could use Boost to implement a bidirectional map - that would allow you to use either of the values as a key. Here is an example of how to use it. But, in short: (usage only, without definitions)
struct from {}; // tag for boost
typedef bidirectional_map<int, std::string>::type bi_map;
bi_map values;
values.insert(bi_map::value_type(123, "{"));
// ...
// ...
bi_map::iterator it = values.get<from>().find(123);
if (it != values.end()) {
cout << "Char #123 is " << it->second << endl;
// and in the opposite case, where "it" is the result of:
// values.get<to>().find("{")
// it->second would be 123, so you have access to both items
}
I'm working on a program right now dealing with some exponential time algorithms. Because of this, a main loop of my program is being run many times, and I'm trying to optimize it as much as possible.
Profiling shows that a large portion of the time is spent in look-up and hash calculation for std::unordered_map.
I'm wondering:
Is there a way to cache the hash value of a key for std::unordered_map, and then provide it as an argument to insert later on?
Is there a way that I can do the following in a single operation: given an key and value {x,y}, check if key x is in the map, if it isn't, insert it and return {x,y}, otherwise return {x,z} for whatever z is already in the map.
I'm doing something like this right now, but it's inefficient, because I have to calculate the hash for the key and check if it's in the map. But if it isn't in the map, I do a completely separate insert operation. In theory, checking if it is present in the map should find where it would go in the map if inserted.
I'm open to trying other data structures, like std::map or something from Boost, if they would reduce the time for this operation.
You could just use the return value of std::unordered_map::insert() to achieve key existence checking + insertion with single hash calculation.
template<typename K, typename V>
std::pair<K, V> myinsert(std::unordered_map<K, V> &map, const std::pair<K, V> &kv)
{
return *(map.insert(kv).first);
}
You can't cache the hash of the key, but if you have an iterator to where it was last time (either from when you originally inserted, or the last time you successfully found the item) you can use the insert( const_iterator hint, value_type&& value ); member which also helpfully returns an iterator to either the newly inserted element or the previously existing element that blocked insertion.
You could just use std::map::emplace().
The insertion only takes place if no other element in the container has a key equivalent to the one being emplaced (keys in a map container are unique).
Example
#include <iostream>
#include <utility>
#include <string>
#include <map>
typedef std::map<std::string, std::string> StringMap;
typedef std::pair<StringMap::iterator, bool> PairKey;
int main()
{
std::map<std::string, std::string> m;
// uses pair's template constructor
m.emplace("a", "aaa");
m.emplace("b", "bbb");
m.emplace("d", "ddd");
PairKey pair = m.emplace("d", "dddddd");
if (pair.second == false)
std::cout<< "d was existed so value didn't change" << std::endl;
std::cout<<"-------MAP_LIST------" << std::endl;
for (const auto &p : m)
std::cout << p.first << " => " << p.second << '\n';
std::cout<<"-------========------" << std::endl;
}
Output:
d was existed so value didn't change
-------MAP_LIST------
a => aaa
b => bbb
d => ddd
-------========------
I have written a bit of code to match two vector of objects that have some of the same instances of objects within both of the vectors.
The idea is to find the index of the object in the 'main' vector and match that to the object of the other vector.
The index of the main vector would then be used in a map with that object.
I think looking at the code may make my explanation a bit clearer:
ifndef OBJECTMAPMATCH_H
#define OBJECTMAPMATCH_H
#include <map>
#include <utility>
#include <vector>
#include <typeinfo>
#include <iostream>
#include <stdlib.h>
namespace ObjectMapMatch {
...
...
template< class A, class B >
std::map<int, B*>* getIndexMap( std::vector<A*>* x , std::vector<B*>* y, std::map<int, B*>* output )
{
typename std::vector<A*>::iterator Aitr = x->begin();
typename std::vector<A*>::iterator AitrE = x->end();
typename std::vector<B*>::iterator Bitr = y->begin();
typename std::vector<B*>::iterator BitrE = y->end();
for(int index=0; Aitr!=AitrE; ++Aitr, ++index){
//Keep track of original index
int AntupIndex = (*Aitr)->Index();
int match = false;
for(; Bitr!=BitrE; ++Bitr){
int BntupIndex = (*Bitr)->Index();
if( AntupIndex == BntupIndex ){
match = true;
output[index] = (*Bitr);
}
} //End of loop B
if(!match){
std::cout << "ERROR:ObjectMapMatch::getIndexMap: Can not Find Match" << typeid(y).name() << " FOR " << typeid(x).name() << std::endl;
exit(1);
}
}//End of Loop A
}
...
...
}
#endif
As you can see I am basically comparing the two objects with there unique index and if this matches the object will match.
My quesitons:
I know I could have overloaded the comparison operator in the object class, but I was not sure if something like this would be correct??
bool operator==(object1& lhs, object2& rhs){
&lhs == &rhs ? return true : return false;
}
Also,
Is there a shorter/more efficient way of the above code using some STL algorithms (can not use the boost libs) or something smarter??
Mike
There are couple of ways to do this more efficiently than O(n^2).
For example:
Sort elements of the first vector.
Sort elements of the second vector.
Use set_intersection on sorted vectors.
Or:
Put elements of both vectors to multiset (or unordered_multiset).
Keys of the multiset that have more than one element indicate a match.
For both of these methods, you could use pointers or indexes in original vectors instead of the actual elements. Just be careful to provide comparer (to sort, set_intersection and multiset) able to deal with pointers/indexes.