3

I have a QMap that represents a database row. The items are indexed by column name:

QMap<QString, QVariant> mapOfItems_;

I then have a method to retrieve item by columnn name:

QVariant ImportDataSourceRow::byName( const QString& name )
{
    if(mapOfItems_.contains(name))
      return mapOfItems_.value(name);
    else
      throw NoSuchColumn();
}

I would like to also implement method to get item by column index (0 for first column):

QVariant ImportDataSourceRow::byIndex( size_t index )
{
    // Now what?
}

How can I get the value at offset index from the map? Is QMap even guaranteed to be ordered as I need it to be?

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778

3 Answers3

2

The entries of a QMap are guaranteed to be ordered by key, in your case that'd be QString::operator<.

To get the position inside the map, you can use:

const auto it = mapOfItems.find(name);
const auto index = std::distance(mapOfItems.begin(), it);

Note that your byName() method would be more efficient if you used constFind() and throw if the returned iterator equals mapOfItems.constEnd(), instead of doing two lookups (contains and value()).

vahancho
  • 20,808
  • 3
  • 47
  • 55
Frank Osterfeld
  • 24,815
  • 5
  • 58
  • 70
  • I guess I can't use `QMap` then, since the column order is not by name. Thanks for answer. Could you recommend what should I use instead? Maybe two arrays - names and values? – Tomáš Zato May 12 '16 at 11:20
  • If your list of items is not too big, an easy and fast way would be a QVector > object, and for index access you can use at() and for name access you would have to iterate, but if the List is small, that would be acceptable. – Daniel82 May 12 '16 at 11:37
  • or just use a vector with the entries sorted by column index. But then byName() would have O(n) complexity instead of O(log n). – Frank Osterfeld May 12 '16 at 11:39
2

Associative (dictionary-like) containers such as QMap and std::map rarely provide sequential indexing as internally they are typically implemented as tree-like data structures (for example, Red-Black Tree).

Notable exception is boost's flat_map. It is implemented as a pair of contiguous arrays, where mapping of the key and value is expressed with array indices: mapped key and value have the same index. flat_map provides method nth() to access value by index:

boost::container::flat_map<std::string, float> geek_numbers;
geek_numbers.emplace("pi", 3.14f);
geek_numbers.emplace("e", 2.72f);
geek_numbers.emplace(
    "Answer to the Ultimate Question of Life, The Universe, and "
    "Everything",
    42.0f);
auto 0th = geek_numbers.nth(0); // 42.0f
auto 1st = geek_numbers.nth(1); // 2.72f
auto 2nd = geek_numbers.nth(2); // 3.14f

flat_map "emulates" interface and behavior of the std::map and sorts elements by key. You can use custom predicates.

Note that standard containers are suitable only for simplest database-like usecases. In general, databases is a very complicated topic. There are entire theories being built about it. If your dataset is large and you need to perform complicated indexing and queries, think about embedding one of the database engines available (e.g. SQLite, or other, more heavyweight ones).

Ivan Aksamentov - Drop
  • 12,860
  • 3
  • 34
  • 61
  • In another answer, @tomáš-zato says that the column order is not by name. So this information would be lost when using a flat_map, right? – Daniel82 May 12 '16 at 14:35
0

Not recommended, but you can try to do it this way:

QList<QString> list = mapOfItems_.values();
if(index>=0 && index<list.count()) return list.at(index);
return QString();

Don't know how it works with a QVariant though.

techwinder
  • 11
  • 3