Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > stl::map: return default value without inserting a new element?

Reply
Thread Tools

stl::map: return default value without inserting a new element?

 
 
Rui Maciel
Guest
Posts: n/a
 
      04-06-2010
When using STL's map container, if operator[] is used to access the value associated with a
given key which wasn't inserted then a new key:value pair will be created and a reference to
the value will be returned. This means that, although operator[] is a convenient way to add
new key:value pairs to a map, it may end up needlessly adding useless key:value pairs into the
map.

In order to avoid wasting resources populating a map with useless key:value pairs, is there a
clean, unencumbered way to get the value associated with a given key without being forced to
insert new elements or even resort to multiple lines of code? The closest thing I saw was the
map::find() method, but I believe that ends up forcing to write code to compare the given
iterator to map::end() and, if it matches, return a default value.

Is there a simpler way to do this?


Rui Maciel
 
Reply With Quote
 
 
 
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-06-2010
Rui Maciel wrote:

> When using STL's map container, if operator[] is used to access the value
> associated with a given key which wasn't inserted then a new key:value
> pair will be created and a reference to
> the value will be returned. This means that, although operator[] is a
> convenient way to add new key:value pairs to a map, it may end up
> needlessly adding useless key:value pairs into the map.
>
> In order to avoid wasting resources populating a map with useless
> key:value pairs, is there a clean, unencumbered way to get the value
> associated with a given key without being forced to
> insert new elements or even resort to multiple lines of code? The closest
> thing I saw was the map::find() method, but I believe that ends up forcing
> to write code to compare the given iterator to map::end() and, if it
> matches, return a default value.
>
> Is there a simpler way to do this?


Not really. You can, of course, wrap this little thing into a dictionary
class, which internally uses a map and find(). But that is not provided by
the standard. Here is what I use:


template < typename KeyType, typename MappedType >
class dictionary {

typedef std::map< KeyType, MappedType > map_type;

map_type the_map;
MappedType the_default;

public:

typedef KeyType key_type;
typedef MappedType mapped_type;

dictionary ( mapped_type const & val = mapped_type() )
: the_map ()
, the_default ( val )
{}

dictionary & insert ( key_type const & key,
mapped_type const & val ) {
the_map[ key ] = val;
return ( *this );
}

dictionary & erase ( key_type const & key ) {
the_map.erase( key );
return( *this );
}

mapped_type const &
operator() ( key_type const & key ) const {
typename map_type::const_iterator iter =
the_map.find( key );
if ( iter == the_map.end() ) {
return ( the_default );
}
return ( iter->second );
}

};


The insert() and erase() method allow for chaining.


Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
 
Joshua Maurice
Guest
Posts: n/a
 
      04-06-2010
On Apr 6, 3:46*am, Rui Maciel <(E-Mail Removed)> wrote:
> When using STL's map container, if operator[] is used to access the value associated with a
> given key which wasn't inserted then a new key:value pair will be created and a reference to
> the value will be returned. *This means that, although operator[] is a convenient way to add
> new key:value pairs to a map, it may end up needlessly adding useless key:value pairs into the
> map.
>
> In order to avoid wasting resources populating a map with useless key:value pairs, is there a
> clean, unencumbered way to get the value associated with a given key without being forced to
> insert new elements or even resort to multiple lines of code? *The closest thing I saw was the
> map::find() method, but I believe that ends up forcing to write code to compare the given
> iterator to map::end() and, if it matches, return a default value.
>
> Is there a simpler way to do this?


Exactly what kind of interface do you want? You want it in one line,
and this seems kind of short for error handling, including errors such
as "entry not found". If you want to simply take a default action on
"entry not found" (such as die), you could write a simple helper
function yourself which returns map::find after doing the error
checking. If you don't want error checking, then just use
std::map::find directly. The only functions in std::map which do a
default action are operator[] and insert, which insert a new entry if
it's not already present.
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-06-2010
Christian Hackl wrote:

> Rui Maciel ha scritto:
>
>> In order to avoid wasting resources populating a map with useless
>> key:value pairs, is there a clean, unencumbered way to get the value
>> associated with a given key without being forced to insert new elements
>> or even resort to multiple lines of code?

>
> But shouldn't you rather view it as an error to access an element which
> does not exist? Perhaps what you should do is access elements with a
> function like this:
>
> template <class KeyType, class MappedType>
> MappedType const &get(std::map<KeyType, MappedType> const &map, KeyType
> const &key)
> {
> std::map<KeyType, MappedType>::const_iterator find_iter =
> map.find(key);
>
> assert(find_iter != map.end());
> return find_iter->second;
> }
>
> (Note how this, in contrast to operator[], can be used with a const
> std::map as well.)
>
> Then make sure outside code accesses only those elements which exist. It
> seems a more robust solution to me than relying on default values being
> returned in case the key does not exist. Otherwise, what happens when
> the default value happens to be one of the actual values in the map? You
> won't be able to distinguish between legitimate access and error cases.


Hitting a value not stored in the table does not always indicate an error. I
recently had an interactive program that was mainly table driven. The key
data structure had the type:

dictionary< char, action >

and the default action was null_op. The main loop would just wait until the
user presses a key and execute the corresponding action. Only keys that
actually do something need to be stored, and all others default to null_op.

In short: whether asking for a non-stored value is an error depends on the
context.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Rui Maciel
Guest
Posts: n/a
 
      04-06-2010
Christian Hackl wrote:

> But shouldn't you rather view it as an error to access an element which
> does not exist?


Not necessarily. In this case it would be nice if it behaved just as operator[] minus the
new key:value insertion, which means returning the value if a key:value pair was found and
otherwise return a default value.


> Perhaps what you should do is access elements with a
> function like this:
>
> template <class KeyType, class MappedType>
> MappedType const &get(std::map<KeyType, MappedType> const &map, KeyType
> const &key)
> {
> std::map<KeyType, MappedType>::const_iterator find_iter =
> map.find(key);
>
> assert(find_iter != map.end());
> return find_iter->second;
> }
>
> (Note how this, in contrast to operator[], can be used with a const
> std::map as well.)


That's not quite it. I was looking for a STL way to do something such as:

<source lang=cpp>
template <class KeyType, class MappedType>
MappedType const &get(std::map<KeyType, MappedType> const &map, KeyType
const &key)
{
std::map<KeyType, MappedType>::const_iterator find_iter =
map.find(key);
if(find_iter == map.end())
return MappedType();
else
return find_iter->second;
}
</source>

> Then make sure outside code accesses only those elements which exist. It
> seems a more robust solution to me than relying on default values being
> returned in case the key does not exist. Otherwise, what happens when
> the default value happens to be one of the actual values in the map?


In my case there's no problem with that.

> You
> won't be able to distinguish between legitimate access and error cases.


In my case those aren't errors, which means this is a non-issue.


Rui Maciel

 
Reply With Quote
 
Keith H Duggar
Guest
Posts: n/a
 
      04-06-2010
On Apr 6, 6:46 am, Rui Maciel <(E-Mail Removed)> wrote:
> In order to avoid wasting resources populating a map with
> useless key:value pairs, is there a clean, unencumbered way
> to get the value associated with a given key without being
> forced to insert new elements or even resort to multiple
> lines of code? The closest thing I saw was the map::find()
> method, but I believe that ends up forcing to write code to
> compare the given iterator to map::end() and, if it matches,
> return a default value.
>
> Is there a simpler way to do this?


I find these two functions (including their general semantics)
and variants of them exceedingly useful

template < class K, class V, class C, class A>
V
getOrZero (
std::map<K,V,C,A> const & m
, K const & k
) {
typename std::map<K,V,C,A>::const_iterator i = m.find(k) ;
return i != m.end() ? i->second : V() ;
}

template < class K, class V, class C, class A>
V &
getOrMake (
std::map<K,V,C,A> & m
, K const & k
) {
return m[k] ;
}

for all types of containers including STL and custom ones.

KHD
 
Reply With Quote
 
Rui Maciel
Guest
Posts: n/a
 
      04-06-2010
Leigh Johnston wrote:

> If you are not happy with maps' interface then you can augment its
> interface through derivation, see
> http://www.i42.co.uk/stuff/mutable_set.htm


I was trying to avoid that. I assumed that this sort of stuff was already provided. Oh
well...


Thanks for the help,
Rui Maciel
 
Reply With Quote
 
Rui Maciel
Guest
Posts: n/a
 
      04-06-2010
Kai-Uwe Bux wrote:

> Not really. You can, of course, wrap this little thing into a dictionary
> class, which internally uses a map and find(). But that is not provided by
> the standard. Here is what I use:
>

<snip code/>

That's exactly what I was looking for. Even the use of operator() matches what I expected
from std::map. Is there a reason why this feature isn't already implemented?


Thanks for the help,
Rui Maciel

 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-06-2010
Rui Maciel wrote:

> Kai-Uwe Bux wrote:
>
>> Not really. You can, of course, wrap this little thing into a dictionary
>> class, which internally uses a map and find(). But that is not provided
>> by the standard. Here is what I use:
>>

> <snip code/>
>
> That's exactly what I was looking for.


That's nice to hear.

> Even the use of operator() matches what I expected
> from std::map. Is there a reason why this feature isn't already
> implemented?


I can only offer conjectures. First, storing the default value is an
overhead unnecessary for most applications of std::map<>. Second, and
probably more serious a consequence: It would be somewhat cumbersome to use
the modified map<> template with value_types that don't support default
construction because, in those cases, you would have to designate a
not_found_value manually.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      04-06-2010
Leigh Johnston wrote:

>
>
> "Keith H Duggar" <(E-Mail Removed)> wrote in message
> news:(E-Mail Removed)...
>> On Apr 6, 6:46 am, Rui Maciel <(E-Mail Removed)> wrote:
>>> In order to avoid wasting resources populating a map with
>>> useless key:value pairs, is there a clean, unencumbered way
>>> to get the value associated with a given key without being
>>> forced to insert new elements or even resort to multiple
>>> lines of code? The closest thing I saw was the map::find()
>>> method, but I believe that ends up forcing to write code to
>>> compare the given iterator to map::end() and, if it matches,
>>> return a default value.
>>>
>>> Is there a simpler way to do this?

>>
>> I find these two functions (including their general semantics)
>> and variants of them exceedingly useful
>>
>> template < class K, class V, class C, class A>
>> V
>> getOrZero (
>> std::map<K,V,C,A> const & m
>> , K const & k
>> ) {
>> typename std::map<K,V,C,A>::const_iterator i = m.find(k) ;
>> return i != m.end() ? i->second : V() ;
>> }
>>
>> template < class K, class V, class C, class A>
>> V &
>> getOrMake (
>> std::map<K,V,C,A> & m
>> , K const & k
>> ) {
>> return m[k] ;
>> }
>>
>> for all types of containers including STL and custom ones.
>>

>
> These free functions can have their uses (they certainly save typing at
> least) but I feel they may detract from good iterator based design.


I am always eager to see new design ideas, but I have some trouble picturing
an iterator to iterate over keys not in the table and providing default
values for their un-tabulated value. Could you please flesh out what you
have in mind?


Best

Kai-Uwe Bux
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
"return delete (new int)" compile but "return delete (new X X C++ 4 07-19-2010 05:47 PM
Inserting default value into a databound textbox sfuller8@gmail.com ASP .Net Web Controls 0 10-27-2006 10:34 AM
Default value when inserting new record with the DetailsView Nick ASP .Net 0 02-24-2006 09:01 AM
what value does lack of return or empty "return;" return Greenhorn C Programming 15 03-06-2005 08:19 PM
getting return value from function without return statement. Seong-Kook Shin C Programming 1 06-18-2004 08:19 AM



Advertisments