Results 1 to 18 of 18

Thread: Nested QHash in range for loop

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Nested QHash in range for loop

    Hi,

    It is possible to use this data structure:

    Qt Code:
    1. struct Data{
    2. QString scope;
    3. QString name;
    4. QVariant value;
    5. };
    6.  
    7. QHash<QString, QHash<QString, Data>> data;
    To copy to clipboard, switch view to plain text mode 

    in range for loop like that:

    Qt Code:
    1. for( const Data & d : data )
    2. qDebug()<< d.scope << d.name << d.value;
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    I know that I must implement begin() and end() inside class which contains these nested QHash.
    But how connect all ranges from inner QHash?
    Last edited by Scope; 3rd December 2015 at 08:48.

  3. #3
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    The nested QHashes must be wrapped in a class for which you need to implement your own iterator and const_iterator. Such an iterator could consist of a triple ((const)iterator on the outer QHash, (const)iterator on the current inner QHash, (c)end() of the current inner QHash).

    Do you really need to go through all this trouble? Consider theses options:
    1. Replace your nested QHash with a flat QHash whose key type is a pair of QStrings.
    2. Keep the nested QHash, and iterate over it with two nested for loops.

    I recommend option 1. Option 2 (and your nested structure in general) only makes sense if you need to collectively manage all bindings sharing the same outer key.

  4. #4
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    QPair can not be a key in my case.
    Every Data structure is identified by scope ( first QString ) and name (second QString ) and now when i want set some Data i have function:

    Qt Code:
    1. class Container
    2. {
    3. public:
    4. void setData(const Data &data);
    5.  
    6. private:
    7. QHash<QString, QHash<QString, Data>> d;
    8.  
    9. };
    10.  
    11.  
    12. void Container::setData(const Data &data)
    13. {
    14. const QString scope = data.scope;
    15. const QString name = data.name;
    16.  
    17. if( d.contains(scope) ) {
    18. if( d.value(scope).contains(name) )
    19. d[scope][name] = data;
    20. else
    21. d[scope].insert(name, data);
    22. }
    23. else {
    24. d.insert(scope, QHash<QString, Data>{{name, data}});
    25. }
    26. }
    To copy to clipboard, switch view to plain text mode 

    and now I want allow to use this class in range for loop like that

    Qt Code:
    1. Container container;
    2. for(const Data &data : container )
    3. qDebug()<< data.scope << data.name << data.value;
    To copy to clipboard, switch view to plain text mode 

    but if I understand you correctly it will be very difficult.

  5. #5
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    Quote Originally Posted by Scope View Post
    QPair can not be a key in my case.
    Really? What about this:
    Qt Code:
    1. class Container
    2. {
    3. public:
    4. void setData(const Data &data);
    5.  
    6. private:
    7. QHash<QPair<QString, QString>, Data> d;
    8.  
    9. };
    10.  
    11. void Container::setData(const Data &data)
    12. {
    13. d.insert(qMakePair(data.scope, data.name), data);
    14. }
    To copy to clipboard, switch view to plain text mode 

    And here is how you iterate on the container:

    Qt Code:
    1. class Container {
    2. // ...
    3. public:
    4. using const_iterator = QHash<QPair<QString, QString>, Data>::const_iterator;
    5. const_iterator cbegin() const {return d.cbegin();}
    6. const_iterator cend() const {return d.cend();}
    7. const_iterator begin() const {return d.begin();}
    8. const_iterator end() const {return d.end();}
    9. // I omit the non-const iterators on purpose, because they would be more complex than const_iterator, as we have an invariant to maintain on d
    10. // ...
    11. };
    12.  
    13. // ...
    14. Container container;
    15. // ...
    16. for(const Data &data : static_cast<const Container &>(container))
    17. qDebug()<< data.scope << data.name << data.value;
    To copy to clipboard, switch view to plain text mode 

    Disclaimer: I have not tried to compile these snippets.
    Last edited by yeye_olive; 3rd December 2015 at 17:18.

  6. #6
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    Really? What about this:
    Yes, because in my case, scope( first string ) must be unique and name of data ( second string ) also must be unique in their scope. Then your example with QPair as key has horrible bad performance and complexity when we compare this to nested hash.

  7. #7
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    Quote Originally Posted by Scope View Post
    Yes, because in my case, scope( first string ) must be unique and name of data ( second string ) also must be unique in their scope.
    Which is equivalent to saying that there must be only one Data for each unique (scope, name) combination. Which is why I suggested the pair as key in the first place.
    Quote Originally Posted by Scope View Post
    Then your example with QPair as key has horrible bad performance and complexity when we compare this to nested hash.
    How do you know? Have you run benchmarks? I would like to hear your argument, because I actually believe the opposite to be true in general. You do realize that a hash table has amortized constant average complexity, right? Besides, a flat QHash wastes less memory than nested ones.

    If nesting was so much better, then we would never see things like QHash<QString, Foo>. Instead, people would use crazy structures like QHash<QString, QHash<QString, Foo> > in which you split the original string into two substrings to index into the two levels. But guess what, they don't.

    Seriously though, there is nothing wrong with a hash table indexed by a pair of QStrings. It is about as efficient a structure as you can get with generic containers. As I already pointed out, nesting is useful if you have operations like slicing (e.g. quickly get all bindings for a given scope) or a very specific distribution of (scope, name) pairs. But all we know is that you are interested in iterating over the whole (flat) collection. Other people seem to agree with me:
    http://stackoverflow.com/questions/2...han-nested-map
    http://programmers.stackexchange.com...-combined-keys

    But hey, if you want to waste your time with overly complex data structures with no gain in performance or memory usage (and most likely a loss), be my guest.

  8. #8
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    How do you know? Have you run benchmarks? I would like to hear your argument
    As you said when we want take all values from specific scope

    Qt Code:
    1. // with QPair as key
    2. QList<Data> Container::data(const QString &scope)
    3. {
    4. QList<Data> data;
    5. for( const QPair<QString, QString> &key : d.keys() )
    6. if( key.first == scope )
    7. data.append( d.value(key));
    8.  
    9. return data;
    10. }
    11.  
    12. // with nested QHash
    13. QList<Data> Container::data(const QString &scope)
    14. {
    15. if( d2.contains(scope) )
    16. return d2.value(scope).values();
    17.  
    18. return QList<Data>();
    19. }
    To copy to clipboard, switch view to plain text mode 

    Maybe first version can be better using std::find_if or std::binary_search but second version is faster.
    But maybe you have right that QPair as key is not worse as I said...

    I am just trying find the best structure, for properties like scope, name and value.
    Main goal is high performance for insert and lookup, and possibility use in range for loop.
    Last edited by Scope; 3rd December 2015 at 20:52.

  9. #9
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    OK, if you need such slicing, then the nested QHashes are the reasonable solution; in that case your setData method would be more efficiently implemented by:
    Qt Code:
    1. void Container::setData(const Data &data) {
    2. d[data.scope].insert(data.name, data);
    3. }
    To copy to clipboard, switch view to plain text mode 
    You could implement a flat iterator by a triple (see my first post).

  10. #10
    Join Date
    Nov 2015
    Posts
    41
    Thanks
    5
    Thanked 3 Times in 3 Posts
    Qt products
    Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    Thank you very much,
    I found in boost iterator_facade template and example

    Qt Code:
    1. # include "node.hpp"
    2. # include <boost/iterator/iterator_facade.hpp>
    3.  
    4. class node_iterator
    5. : public boost::iterator_facade<
    6. node_iterator
    7. , node_base
    8. , boost::forward_traversal_tag
    9. >
    10. {
    11. public:
    12. node_iterator()
    13. : m_node(0) {}
    14.  
    15. explicit node_iterator(node_base* p)
    16. : m_node(p) {}
    17.  
    18. private:
    19. friend class boost::iterator_core_access;
    20.  
    21. void increment() { m_node = m_node->next(); }
    22.  
    23. bool equal(node_iterator const& other) const
    24. {
    25. return this->m_node == other.m_node;
    26. }
    27.  
    28. node_base& dereference() const { return *m_node; }
    29.  
    30. node_base* m_node;
    31. };
    To copy to clipboard, switch view to plain text mode 

    but i did not know how jump from end of one QHash to begin another?

  11. #11
    Join Date
    Oct 2009
    Posts
    483
    Thanked 97 Times in 94 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Nested QHash in range for loop

    Here you go, a proof of concept in pseudocode of the flat const iterator. It only supports dereferencing and incrementation. It is in fact a quadruple (current and end iterator for outer and inner maps):
    Qt Code:
    1. Data members:
    2. // Iterator on the outer QHash
    3. QHash<QString, QHash<QString, Data> >::const_iterator m_outer;
    4. // End iterator on the outer QHash
    5. QHash<QString, QHash<QString, Data> >::const_iterator m_outerEnd;
    6. // Iterator on the inner QHash. Has an indeterminate value if m_outer == m_outerEnd; otherwise, points to the current binding in the current inner map
    7. QHash<QString, Data>::const_iterator m_inner;
    8. // End iterator on the inner QHash. Has an indeterminate value if m_outer == m_outerEnd; otherwise, is the end() iterator of the current inner map
    9. QHash<QString, Data>::const_iterator m_innerEnd;
    10.  
    11. // Dereferences the iterator; assumes m_outer != m_outerEnd
    12. Dereference:
    13. Return *m_inner
    14.  
    15. // Tests for equality with another iterator on the same container
    16. Equals(const const_iterator &other):
    17. If m_outer == m_outerEnd
    18. Return other.m_outer == other.m_outerEnd
    19. Else
    20. Return m_outer == other.m_outer && m_inner == other.m_inner
    21.  
    22. // A private operation that updates the state right after m_outer has received a new value
    23. NewOuter:
    24. While m_outer != m_outerEnd
    25. m_inner = m_outer->begin()
    26. m_innerEnd = m_outer->end()
    27. If m_inner != m_innerEnd
    28. Break
    29. Increment m_outer
    30.  
    31. // Sets up this iterator at the beginning of the collection c
    32. Begin(const QHash<QString, QHash<QString, Data> > &c):
    33. m_outer = c.begin()
    34. m_outerEnd = c.end()
    35. Call NewOuter
    36.  
    37. // Sets up this iterator at the end of the collection c
    38. End(const QHash<QString, QHash<QString, Data> > &c):
    39. m_outer = m_outerEnd = c.end()
    40.  
    41. // Increments this iterator; assumes m_outer != m_outerEnd
    42. Increment:
    43. Increment m_inner
    44. If m_inner == m_innerEnd
    45. // Done iterating on the current inner map; move to the next outer binding
    46. Increment m_outer
    47. Call NewOuter
    To copy to clipboard, switch view to plain text mode 

  12. The following user says thank you to yeye_olive for this useful post:

    Scope (6th December 2015)

Similar Threads

  1. Loop or Nested Iterator
    By enricong in forum Qt Programming
    Replies: 3
    Last Post: 28th September 2011, 06:06
  2. Replies: 4
    Last Post: 6th August 2011, 01:40
  3. Main loop thread loop communication
    By mcsahin in forum Qt Programming
    Replies: 7
    Last Post: 25th January 2011, 16:31
  4. Replies: 1
    Last Post: 10th February 2009, 09:42
  5. Can QHash::capacity() be smaller than QHash::size()?!?
    By iw2nhl in forum Qt Programming
    Replies: 2
    Last Post: 24th August 2007, 01:17

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.