std::multimap with composite key?

P

PeteOlcott

I want to make a std::multimap and use two std::string data members as
the key to the data record, is there a standard C++ way to do this?

struct NameValuePair
{
std::string Name;
std::string Value;
std::string ParentName;
std::string ParentValue;
};

This is semantically what I want to do, what is the correct standard C+
+ syntax?
std::mulitmap<std::string Name + std::string Value, NameValuePair nvp>
data;
 
S

sean_in_raleigh

I want to make a std::multimap and use two std::string data members as
the key to the data record, is there a standard C++ way to do this?

struct NameValuePair
{
std::string Name;
std::string Value;
std::string ParentName;
std::string ParentValue;

};

This is semantically what I want to do, what is the correct standard C+
+ syntax?
std::mulitmap<std::string Name + std::string Value, NameValuePair nvp>
data;

NameValuePair nvp = { "some_name", "some_value", "...", "..." };

typedef pair<std::string, std::string> CompositeKey;
std::multimap<CompositeKey, NameValuePair> data;

CompositeKey my_key_pair = make_pair(nvp.Name, nvp.Value);
data.insert(make_pair(my_key_pair, nvp));
 
M

Maxim Yegorushkin

I want to make a std::multimap and use two std::string data members as
the key to the data record, is there a standard C++ way to do this?

struct NameValuePair
{
  std::string Name;
  std::string Value;
  std::string ParentName;
  std::string ParentValue;
};

You can store NameValuePair in std::set and use a custom less functor,
that only compares NameValuePair::Name and NameValuePair::Value to
establish strict weak ordering:

struct NameValuePair
{
std::string Name;
std::string Value;
std::string ParentName;
std::string ParentValue;

struct Less
{
bool operator()(NameValuePair const& a, NameValuePair const&
b) const
{
if(int r = a.Name.compare(b.Name))
return r < 0;
return a.Value.compare(a.Value) < 0;
}
};
};

typedef std::set<NameValuePair, NameValuePair::Less> NameValuePairSet;

The problem with the above approach is that because std::map::find()
requires a complete value for searching, i.e. you have to construct a
complete NameValuePair object, although you only search by
NameValuePair::Name and NameValuePair::Value members. Searching
NameValuePairSet looks like this:

NameValuePair const* find(
NameValuePairSet const& s
, std::string const& name
, std::string const& value
)
{
NameValuePair dummy_object_for_find = { name, value }; // <---
here
NameValuePairSet::const_iterator i = s.find
(dummy_object_for_find);
return i != s.end() ? &*i : NULL;
}

It may not always be convenient to construct dummy_object_for_find
just to satisfy std::set<> interface. And it is not elegant at all.

For better results I suggest you use boost::multi_index. This is how:

#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>

struct NameValuePair
{
struct Key {
std::string Name;
std::string Value;
} key;

std::string ParentName;
std::string ParentValue;

struct LessKey
{
bool operator()(Key const& a, Key const& b) const
{
if(int r = a.Name.compare(b.Name))
return r < 0;
return a.Value.compare(a.Value) < 0;
}
};

struct ExtractKey
{
typedef Key result_type;
Key const& operator()(NameValuePair const& a) const
{
return a.key;
}
};
};

namespace M = boost::multi_index;

typedef M::multi_index_container<
NameValuePair
, M::indexed_by<
M::eek:rdered_unique<NameValuePair::ExtractKey,
NameValuePair::LessKey>
>
NameValuePairSet;

NameValuePair const* find(
NameValuePairSet const& s
, std::string const& name
, std::string const& value
)
{
NameValuePair::Key key = { name, value };
NameValuePairSet::const_iterator i = s.find(key);
return i != s.end() ? &*i : NULL;
}

Please note that in this version of find only NameValuePair::Key
object is constructed.

See http://www.boost.org/doc/libs/1_37_0/libs/multi_index/doc/tutorial/index.html
for more details.
 
D

Daniel T.

I want to make a std::multimap and use two std::string data members as
the key to the data record, is there a standard C++ way to do this?

struct NameValuePair
{
  std::string Name;
  std::string Value;
  std::string ParentName;
  std::string ParentValue;

};

This is semantically what I want to do, what is the correct standard C+
+ syntax?
std::mulitmap<std::string Name + std::string Value, NameValuePair nvp>
data;


In addition to the other advice given. Note that if you use a map, the
name + value in the key won't be connected to the name + value in the
struct except by coincidence.

Is it your intention to be able to find all the objects with the same
name or same value? You couldn't do that using a map, you could only
(easily) find all the object with the same name + value pair.
 
P

PeteOlcott

In addition to the other advice given. Note that if you use a map, the
name + value in the key won't be connected to the name + value in the
struct except by coincidence.

Is it your intention to be able to find all the objects with the same
name or same value? You couldn't do that using a map, you could only
(easily) find all the object with the same name + value pair.- Hide quoted text -

- Show quoted text -

// Here is my solution:

#include <stdio.h>
#include <map>
#include <string>

struct NameValuePair
{
std::string Name;
std::string Value;
};

int main()
{
NameValuePair nvp;
std::multimap<std::string, NameValuePair> nvp_multimap;

nvp.Name = "Name"; nvp.Value = "Pete";
nvp_multimap.insert(std::make_pair(nvp.Name + nvp.Value, nvp));

nvp.Name = "Name"; nvp.Value = "Pete";
nvp_multimap.insert(std::make_pair(nvp.Name + nvp.Value, nvp));

printf("nvp_map.size(%d)\n", nvp_multimap.size());

std::multimap<std::string, NameValuePair>::iterator result;
result = nvp_multimap.find("NamePete");

if (result != nvp_multimap.end())
printf("Found It!\n");
else
printf("Not Found!\n");
}
 
D

Daniel T.

// Here is my solution:

#include <stdio.h>
#include <map>
#include <string>

struct NameValuePair
{
std::string Name;
std::string Value;

};

int main()
{
NameValuePair nvp;
std::multimap<std::string, NameValuePair> nvp_multimap;

nvp.Name = "Name"; nvp.Value = "Pete";
nvp_multimap.insert(std::make_pair(nvp.Name + nvp.Value, nvp));

nvp.Name = "Name"; nvp.Value = "Pete";
nvp_multimap.insert(std::make_pair(nvp.Name + nvp.Value, nvp));

printf("nvp_map.size(%d)\n", nvp_multimap.size());

std::multimap<std::string, NameValuePair>::iterator result;
result = nvp_multimap.find("NamePete");

if (result != nvp_multimap.end())
{
// the problem with this solution is: what if
// someone does this?
result->Name = "Joe";

// the key for the object will stll be "NamePete"
// but the value will be "JoePete"

// you should probably use a set/multiset instead.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,527
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top