473,386 Members | 1,803 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Preserving order of insertion

I am trying to write a conatiner which has std::map like methods but
preserves the order of insertion.
TO achieve this I thought of providing my own function for comparing
the keys of the map. Following is the sample code for this:

template <class T>
struct my_compare
{
bool operator () (const T&a, const T&b)
{
return false;
}
};

int main()
{
std::map<std::string,int, my_compare<std::string a;

std::string str = "1";
a[str] = 1;

str = "3";
a[str] = 3;

str = "2";
a[str] = 2;

str = "5";
a[str] = 5;
int size = a.size();

std::map<std::string,int, my_compare<std::string ::iterator iter =
a.begin();
for(; iter != a.end(); ++iter)
{
cout<<iter->first.c_str() <<"\n";
}

return 1;
}

The output of this code is : 1 i.e. there is only one element in the
map. Can somebody explain why ?

If I return true in operator(), the output is 5 2 3 1 (as expected).

I am compiling it using VC 6 compiler.

Oct 17 '06 #1
5 9464
Naveen wrote:
I am trying to write a conatiner which has std::map like methods but
preserves the order of insertion.
Try using a map and a vector of iterators into the map. The map does its
thing and after inserting an element into the map, you append its location
to the vector, which then keeps track of the order of insertion. This will
allow you, by adding one level of indirection, to traverse the map in the
order the entries were inserted.

TO achieve this I thought of providing my own function for comparing
the keys of the map. Following is the sample code for this:

template <class T>
struct my_compare
{
bool operator () (const T&a, const T&b)
{
return false;
}
};

int main()
{
std::map<std::string,int, my_compare<std::string a;

std::string str = "1";
a[str] = 1;

str = "3";
a[str] = 3;

str = "2";
a[str] = 2;

str = "5";
a[str] = 5;
int size = a.size();

std::map<std::string,int, my_compare<std::string ::iterator iter =
a.begin();
for(; iter != a.end(); ++iter)
{
cout<<iter->first.c_str() <<"\n";
}

return 1;
}

The output of this code is : 1 i.e. there is only one element in the
map. Can somebody explain why ?
The map container does not use operator== to check whether entries are
equal. Instead, it relies only upon your comparison predicate comp. Then,
equivalence of keys is defined as follows:

key_a is equivalent to key_b
if and only if
neither comp( key_a, key_b ) nor comp( key_b, key_a )

Your predicate returns always false whence all keys are considered
equivalent. Thus, when you try to insert the second key the map will think
that it already has an entry with an equivalent key.

If I return true in operator(), the output is 5 2 3 1 (as expected).
In this case, all keys will be considered inequivalent, even when you
compare a key to itself. Thus, you should expect WeirdThings(tm) when you
use the same key twice.
[snip]

Best

Kai-Uwe Bux
Oct 17 '06 #2
Naveen wrote:
I am trying to write a conatiner which has std::map like methods but
preserves the order of insertion.
To achieve this I thought of providing my own function for comparing
the keys of the map. Following is the sample code for this:
That's very complex - push_back on vector/deque/list will also preserve
order of insertion. Which std::map methods would you want?
template <class T>
struct my_compare
{
bool operator () (const T&a, const T&b)
{
return false;
}
};
That says that all T elements are equal. Equality in std::map means
that
compare(a,b) and compare(b,a) are false. Obviously, since you can't
have
duplicates in a map, such a map can store only one item. The second
item
you try to insert will overwrite the first one, since it has the same
key.
int main()
{
std::map<std::string,int, my_compare<std::string a;

std::string str = "1";
a[str] = 1;

str = "3";
a[str] = 3;

str = "2";
a[str] = 2;

str = "5";
a[str] = 5;
}

There is only one element in the map. Can somebody explain why ?
Sure: because you said that "1" is not less than "3" and "3" is not
less than
"1". Obviously they are the same key, then.
If I return true in operator(), the output is 5 2 3 1 (as expected).
That means your compiler doesn't double-check your comparison function.
It's not required to. You are required to return false for either
my_compare(a,b)
or my_compare(b,a). A modern compiler can spot this, but typically they
only bother in debug mode. In release mode, they are optimized to
assume
my_compare(b,a) is false if my_compare(a,b) is true.

In your example, you'd get the weird situations that
my_compare("1","1") is
true. I.e. "1" comes before "1" !

If you would want to use any of the map-specific functions, you'll see
they all use
my_compare. operator[ ](x) will return the equality test I described
earlier: find
the key for which my_compare(key,x) and my_compare(x, key) are both
false.

HTH,
Michiel Salters

Oct 17 '06 #3
Kai-Uwe Bux wrote:
Naveen wrote:
>I am trying to write a conatiner which has std::map like methods but
preserves the order of insertion.

Try using a map and a vector of iterators into the map. The map does its
thing and after inserting an element into the map, you append its location
to the vector, which then keeps track of the order of insertion. This will
allow you, by adding one level of indirection, to traverse the map in the
order the entries were inserted.
I should remark that this makes deletions a little costly: you need a linear
search in the vector to find the iterator and remove it. Here is an idea
that should to logarithmic time:

#include <map>
#include <list>
#include <iostream>

template < typename K, typename M >
struct my_map {

typedef std::map<K,MKM_map;
typedef typename KM_map::value_type value_type;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef std::list< pointer P_list;
typedef std::map< pointer, typename P_list::iterator P_map;

KM_map the_KM_map;
P_list the_P_list;
P_map the_P_map;
typename KM_map::iterator
insert ( value_type const & value ) {
typename KM_map::iterator iter = the_KM_map.insert( value ).first;
// FIXME: [should use boost::addressof]
pointer ptr = &( *iter );
the_P_list.push_back( ptr );
the_P_map[ ptr ] = -- the_P_list.end();
return ( iter );
}

void erase ( typename KM_map::iterator where ) {
// FIXME: [should use boost::addressof]
pointer ptr = &( *where );
typename P_map::iterator pm_iter = the_P_map.find( ptr );
typename P_list::iterator pl_iter = pm_iter->second;
the_P_list.erase( pl_iter );
the_P_map.erase( pm_iter );
the_KM_map.erase( where );
}

};

typedef my_map<int,int>::KM_map::value_type int_pair;

int main ( void ) {
my_map<int,intim;
im.insert( int_pair( 4, 5 ) );
im.insert( int_pair( 2, 1 ) );
im.insert( int_pair( 3, 1 ) );
im.erase( im.the_KM_map.find( 2 ) );
for ( my_map<int,int>::P_list::const_iterator iter =
im.the_P_list.begin();
iter != im.the_P_list.end(); ++iter ) {
std::cout << (*iter)->first << " " << (*iter)->second << '\n';
}
}

It's just a proof of concept. Hope it helps.
Best

Kai-Uwe Bux
Oct 17 '06 #4
Kai-Uwe Bux wrote:
...
The map container does not use operator== to check whether entries are
equal. Instead, it relies only upon your comparison predicate comp. Then,
equivalence of keys is defined as follows:

key_a is equivalent to key_b
if and only if
neither comp( key_a, key_b ) nor comp( key_b, key_a )

Your predicate returns always false whence all keys are considered
equivalent. Thus, when you try to insert the second key the map will think
that it already has an entry with an equivalent key.

>If I return true in operator(), the output is 5 2 3 1 (as expected).

In this case, all keys will be considered inequivalent, even when you
compare a key to itself. Thus, you should expect WeirdThings(tm) when you
use the same key twice.
...
(As a side note:) Strictly speaking, the implementation of an associative
container is free to use the following definition of the equivalence

key_a is equivalent to key_b
if and only if
both !comp( key_a, key_b ) and !comp( key_b, key_a )

which is the same as yours for a correctly defined comparison predicate.

But in this case the _second_ implementation (the one that always returns
'true') will cause all keys to be treated as equivalent, when the first one
(returns 'false') will make them all non-equivalent.

In other words, in general case an incorrectly defined comparison predicate will
always lead to WeirdThings(tm) happening and the first form is not really more
predictable than the second one.

--
Best regards,
Andrey Tarasevich
Oct 17 '06 #5
Andrey Tarasevich wrote:
Kai-Uwe Bux wrote:
>...
The map container does not use operator== to check whether entries are
equal. Instead, it relies only upon your comparison predicate comp. Then,
equivalence of keys is defined as follows:

key_a is equivalent to key_b
if and only if
neither comp( key_a, key_b ) nor comp( key_b, key_a )

Your predicate returns always false whence all keys are considered
equivalent. Thus, when you try to insert the second key the map will think
that it already has an entry with an equivalent key.

>>If I return true in operator(), the output is 5 2 3 1 (as expected).

In this case, all keys will be considered inequivalent, even when you
compare a key to itself. Thus, you should expect WeirdThings(tm) when you
use the same key twice.
...

(As a side note:) Strictly speaking, the implementation of an associative
container is free to use the following definition of the equivalence

key_a is equivalent to key_b
if and only if
both !comp( key_a, key_b ) and !comp( key_b, key_a )

which is the same as yours for a correctly defined comparison predicate.

But in this case the _second_ implementation (the one that always returns
'true') will cause all keys to be treated as equivalent, when the first one
(returns 'false') will make them all non-equivalent.
Oops... Sorry. What I said here just doesn't make any sense. You are right,
there's a certain distinction between the "always false" and "always false"
versions of the predicate.

--
Best regards,
Andrey Tarasevich
Oct 17 '06 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

39
by: Nicolas Fleury | last post by:
In the following example: class MyMetaclass(type): pass class MyBaseType(object): __metaclass__ = MyMetaclass class MyType(MyBaseType): x = 4 y = 5 z = 6 Is there any way to modify...
12
by: Tanguy Fautré | last post by:
Hello, does std::multimap make any guarantee about the insertion order? for example: int main() { std::multimap<int, int> Map;
0
by: Murad Nayal | last post by:
Hello, I vaguely remember reading in the manual that the order of the retrieved rows in a response to a select statement is unpredictable (unless you use an order by clause). this possibly...
20
by: Patrick Guio | last post by:
Dear all, I have some problem with insertion operator together with namespace. I have a header file foo.h containing declaration of classes, typedefs and insertion operators for the typedefs in...
16
by: TTroy | last post by:
Hello, I'm relatively new to C and have gone through more than 4 books on it. None mentioned anything about integral promotion, arithmetic conversion, value preserving and unsigned preserving. ...
1
by: (PeteCresswell) | last post by:
Got a text control that where .Locked = True. I have the text blue and underlined to simulate a clickable link on a web page. If the user clicks on that field, I open up a window that shows the...
13
by: bevanward | last post by:
Hi All I am finding unexpected results when inserted into a newly created table that has a field of datatype int identity (1,1). Basically the order I sort on when inserting into the table is...
3
by: Shum | last post by:
Hi .. another quey... i just noticed that the entries being done to the sql server tables ( insert query in C# )are not in the correct order.. the last entry is not always the last record in the...
3
by: sophia.agnes | last post by:
Dear all, I was going through the book "C a software engineering approach by darnell & Margolis" there was a section named sign preserving vs value preserving it is as follows sign...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.