QHash of QPair iteration - c++

QHash<QPair<QString N_id, QString A_id>, QString name> info
I have this QHash , and i have the values of N_id and name for a particular index, how can i obtain the value of corresponding A_id. I am trying to use STL-style iterator. I can change QHash to QMap if needed but I cannot use:
QHash<QPair<QString N_id, QString name>, QString A_id>
Edit: N_id and A_id together forms a unique key in my case.

I think the major problem here is that QHash, being a hash table, looks up the values by hashing the keys. Hence, it needs to complete key to be able to look up a value; a "partial" key won't suffice - there's going to be no concrete object to hash then. A similar problem arises with a map: to navigate the BST, you need the complete object in order to make comparisons and left / right decisions. Thus, short of going back to the drawing board and revising your approach, I'd say, maintain a backwards map, be it a QHash or a QMap, with the mapping name -> pair(n_id, a_id). The downside is that you're going to have to keep the two in sync.
However, with the existing data structure, I'd perform a query like this:
#include <algorithm>
QHash<QPair<QString, QString>, QString> info;
QString a_n_id {/*...*/}; // the target N_id
QString a_name {/*...*/}; // the target name
/* ... */
const auto keyList = info.keys(a_name); // QList<QPair<QString, QString> >
std::find_if(keyList.begin(), keyList.end(),
[&](decltype(info)::key_type& key) { return key.first == a_n_id; });
See this question in case decltype(info)::value_type refuses to build on Microsoft VS.
This is of course going to be linear, since, as I've already said, a hash needs the complete object to be able to perform a lookup, hence we can't use the logarithmic complexity lookup in this case.

Related

How to efficiently lookup items in QAbstractTableModel::data?

From what I have seen, Qt documentation and majority of examples online assume that we are happy with (column, row)-based lookup in data(). But what If my table is based on a custom structure? For instance let's have:
struct MyDrive
{
QString serialNo;
QString user;
QString pc;
QString ipAddress;
QString category;
};
where serialNo is the key. So any operation from outside (imagine the model having implemented a listener) uses it for removing/modifying an item, making QMap as an ideal candidate.
But how to connect this structure with QModelIndex's data? QAbstractTableModel::data asks for data with (column,row) as key, making it more suitable for QVector<QVector>> or something similar (somewhere I read I should avoid using containers with non-constant access time (like map) in data()).
I can imagine using, well, a map with QModelIndex as key and serialNo as value, which would be used as key to my (serialNo-based) map but this looks very inefficient -- QModelIndex addresses concrete entry (serialNo, user, pc, ...) after all so we'd be duplicating the same item over and over again. I was also thinking about having a <serialNo, MyDrive*> map but this is just a workaround to an ugly design decision.
I can't believe I'm the first one with this scenario, so how is it usually solved?
You can use QAbstractItemModel::match to find items by serial
qt help
And enter all the necessary data into the table. This will allow you not to use containers, but how efficient is this a question...
Second solution is subclass AbstractItemModel Reference. Now you can do what you want and use any container by implementing data() function.

How to mutate list inside a QQmlPropertyMap?

I have a config object called config represented with a QQmlPropertyMap. It has a key called huge_list which is a large list of strings.
QVariantList hugeList = makeHugeList(); // makes a huge list of QStrings
QQmlPropertyMap config;
ownerData.insert("huge_list", hugeList);
How can I add, remove, or edit a particular element of huge_list in C++ without replacing the entire list with a new one? The issue is that config["huge_list"] would return a QVariant which does not seem to support mutation of the underlying data.

Writing an SQLite manager class: viable approach?

I'm learning SQLite and C++ within the Qt framework. As a learning project I am doing a simple image viewer which enables the user to tag images with keywords, categories, comments and ROI (for some later OpenCV functionality). It's a pretty simple database with some primary tables and some relational tables.
I think I've got the basics down and early tests show that my record data is being stored but in writing the methods to manage the database it looks like I am going to end up with a great many methods, particularly when I start to add in searching, sorting, deleting, etc.
This leads me to ask if I am going about this the correct way. I can see how some of my query logic (for search, sort) is probably better off being in a different "controller" type class and that all this manager class needs to do is handle the basic creation and deletion tasks, and just return a query object in response to a SELECT statement passed in as a string.
So, am I going about this in a reasonable way?
Manager methods so far:
bool openDatabase(QString name);
void closeDatabase();
bool createTables();
bool addKeyword(QString keyword);
bool addCategory(QString category);
bool deleteKeyword(QString keyword);
bool deleteCategory(QString category);
bool addROI(int x, int y, int width, int height);
bool deleteROI(int id);
bool addImage(QString name, QString path, QByteArray image, QByteArray thumb);
You probably should be using Qt's Model View Framework. The important classes there are QSqlQueryModel, QSqlTableModel and QSqlRelationalTableModel. To keep your UI isolated from the database structure, a reasonable approach would be to add view models for the particular use cases you have. You can then easily link those to, say, a Qt Quick based user interface.
There's nothing particularly wrong with function-oriented interface that you propose, except that it requires a lot of boring glue code to use it for user interfaces. It's best to factor such glue code as a proxy view model, since you're working to a documented API that can be then easily picked up by coworkers.
I have seen a DatabaseManager class somewhere and modified it. This is the insert function :
bool DatabaseManager::insert(const QString &tableName, const QString& columns, const QString &value)
{
bool ret = false;
if(db.isOpen()){
QSqlQuery query;
ret = query.exec( QString("INSERT INTO %1 (%2) VALUES(%3)").arg(tableName, columns, value) );
}
return ret;
}
You give it your table's name, the columns you want to fill and then the values. This is an example where I call it :
if( !dm.insert("Product", "ID, Name, Price, Notes, Category",
"'1', 'A DVD','10€', 'Some Notes', 'DVD'") )
{
//Note that each value is surrounded by an apostrophe
//Now whatever you want to
}

Best tree/heap data structure for fixed set of nodes with changing values + need top 20 values?

I'm writing something like a game in C++ where I have a database table containing the current score for each user. I want to read that table into memory at the start of the game, quickly change each user's score while the game is being played in response to what each user does, and then when the game ends write the current scores back to the database. I also want to be able to find the 20 or so users with the highest scores. No users will be added or deleted during the short period when the game is being played. I haven't tried it yet, but updating the database might take too much time during the period when the game is being played.
Fixed set of users (might be 10,000 to 50,000 users)
Will map user IDs to their score and other user-specific information.
User IDs will be auto_increment values.
If the structure has a high memory overhead that's probably not an issue.
If the program crashes during gameplay it can just be re-started.
Greatly prefer something already available, such as open source/public domain code.
Quickly get a user's current score.
Quickly add to a user's current score (and return their current score)
Quickly get 20 users with highest score.
No deletes.
No inserts except when the structure is first created, and how long that takes isn't critical.
Getting the top 20 users will only happen every five or ten seconds, but getting/adding will happen much more frequently.
If not for the last, I could just create a memory block equal to sizeof(user) * max(user id) and put each user at user id * sizeof(user) for fast access. Should I do that plus some other structure for the Top 20 feature, or is there one structure that will handle all of this together?
Use a std::map. In the incredibly unlikely event that it ever shows up in your profiling, you could maybe think about changing to something more exotic. Memory overhead for 50k users will be around a megabyte or two.
I doubt that iterating over a map with 50k entries every 5-10 seconds, to find the top scores, will introduce significant overhead. If it does, though, either use a Boost multi-index container, or maintain a separate structure for the hi-scores (a heap, or just an array of pointers to the current top 20, in order). Just with an array / vector of 20, the code to increment a score might look something like this (assuming scores only go up, not down):
player.score += points;
if (player.score > hiscores[19]->score) {
hiscore_dirty = true;
}
And the code to get the hi-scores:
if (hiscore_dirty) {
recalculate_hiscores();
hiscore_dirty = false;
}
std::for_each(hiscores.begin(), hiscores.end(), do_something);
If your "auto-increment" and "no delete" policies are fixed forever (i.e. you will never delete users from the DB), and therefore user ids truly are a contiguous range from 0 to the limit, then you should just use a std::vector instead of a std::map.
You might be interested in Fibonacci Heap. This has O(1) (amortized) increaseKey and findMax.
For more info on Heap in general refer: Heap Data Structure, especially the table which compares different heaps.
An implementation of Fibonacci Heap can be found here which you can perhaps use/get inspired from: http://resnet.uoregon.edu/~gurney_j/jmpc/fib.html
First of all, given that you have a Key/Value scenario, you should probably use an Associative Container.
If you are using plain old C++ and do not have Boost available, follow Steve Jessops's suggestion and simply use a std::map, if you have either C++0x or Boost, you'd better use a hash_map or unordered_map: it just matches your requirements better (you don't need to order the players by id after all, you just want to find them quickly) and will probably be faster given the number of players.
For managing the top20 you have 2 choices:
You could use the Boost.MultiIndex library to create one unique container that both offers fast lookup on ID (using a hash map) and an ordered index on the score... however it's a bit of a waste to order all players when you only need 20 of them
You can simply manages a separate structure, like a vector of pointers to users, and each time you modify the score of a user check it should replace a user in the vector
The last solution, though simple, assumes that a player cannot lose points... it's much more difficult if that may happen.
class UsersCollection;
class User
{
public:
void incrementScore(size_t term);
private:
size_t mId;
size_t mScore;
UsersCollection& mCollection;
};
class UsersCollection
{
public:
static const size_t MNumberHiScores = 20;
static const size_t MNotAChampion = -1;
UsersCollection(DBConnection const&);
// returns either the position of the user in
// the hi scores vector or MNotAChampion
size_t insertUserInHiScores(User const& user);
private:
std::unordered_map<size_t, User> mUsers;
std::vector<User const*> mHiScores; // [1]
};
void User::incrementScore(size_t term)
{
mScore += term;
mCollection.insertUserInHiScores(*this);
}
struct UserSort: std::binary_function<User const*, User const*, bool>
{
bool operator()(User const* lhs, User const* rhs) const
{
return lhs->score() > rhs->score();
}
};
size_t UsersCollection::insertUserInHiScores(User const& user)
{
std::vector<User const*>::const_iterator it =
std::find(mHiScores.begin(), mHiScores.end(), &user);
if (it == mHiScores.end()) // not among the hiscores
{
mHiScores.push_back(&user);
}
std::sort(mHiScores.begin(), mHiScores.end(), UserSort());
if (mHiScores.size() > MNumberHiScores) // purge if too many users
{
User const* last = mHiScores.back();
mHiScores.pop_back();
if (&user == last) return MNotAChampion;
}
// return position in the vector in the [0, MNumberHiScores) range
return std::find(mHiScores.begin(), mHiScores.end(), &user)
- mHiScores.begin();
}
Note (1): using a set may seem a good idea however a set presumes that the elements do not change and it is not the case. It could work if we were very careful:
remove the user from the set before changing the score
putting the user back in once it has changed
optionally popping the last elements if there are too many of them

LRU implementation in production code

I have some C++ code where I need to implement cache replacement using LRU technique.
So far I know two methods to implement LRU cache replacement:
Using timeStamp for each time the cached data is accessed and finally comparing the timeStamps at time of replacement.
Using a stack of cached items and moving them to the top if they are accessed recently, so finally the bottom will contain the LRU Candidate.
So, which of these is better to be used in production code?
Are their any other better methods?
Recently I implemented a LRU cache using a linked list spread over a hash map.
/// Typedef for URL/Entry pair
typedef std::pair< std::string, Entry > EntryPair;
/// Typedef for Cache list
typedef std::list< EntryPair > CacheList;
/// Typedef for URL-indexed map into the CacheList
typedef boost::unordered_map< std::string, CacheList::iterator > CacheMap;
/// Cache LRU list
CacheList mCacheList;
/// Cache map into the list
CacheMap mCacheMap;
It has the advantage of being O(1) for all important operations.
The insertion algorithm:
// create new entry
Entry iEntry( ... );
// push it to the front;
mCacheList.push_front( std::make_pair( aURL, iEntry ) );
// add it to the cache map
mCacheMap[ aURL ] = mCacheList.begin();
// increase count of entries
mEntries++;
// check if it's time to remove the last element
if ( mEntries > mMaxEntries )
{
// erease from the map the last cache list element
mCacheMap.erase( mCacheList.back().first );
// erase it from the list
mCacheList.pop_back();
// decrease count
mEntries--;
}
Here is a very simple implementation of LRU cache
https://github.com/lamerman/cpp-lru-cache .
It's easy to use and understand how it works. The total size of code is about 50 lines.
For simplicity, maybe you should consider using Boost's MultiIndex map. If we separate the key from the data, we support multiple sets of keys on the same data.
From [ http://old.nabble.com/realization-of-Last-Recently-Used-cache-with-boost%3A%3Amulti_index-td22326432.html ]:
"...use two indexes: 1) hashed for searching value by key 2) sequential for tracking last recently used items (get function put item as last item in sequesnce. If we need to remove some items from cache, we may delete they from begin of sequence)."
Note that the "project" operator "allows the programmer to move between different indices of the same multi_index_container" efficiently.
This article describes implementation using a pair of STL containers (a key-value map plus a list for the key access history), or a single boost::bimap.
In our production environment we use a C++ double linked list which is similar to the Linux kernel linked list. The beauty of it is that you can add an object to as many linked lists as you want and list operation is fast and simple.
This can be done with boost/compute/detail/lru_cache.hpp. Here is a basic example using it.
#include <boost/compute/detail/lru_cache.hpp>
...
// create an instance that maps from a double to a string and has a max size of 1000
auto my_lru_cache = boost::compute::detail::lru_cache<double, std::string>(1000);
my_lru_cache.insert(3.14, "pi");
if (my_lru_cache.contains(3.14))
{
// the first get returns a boost::optional
auto value = my_lru_cache.get(3.14).get();
std::cout << value << "\n";
}