C++ : Map Tutorial Part 3: Using User defined class objects as keys in std::map

In this article we will discuss how to use User defined classes as key in std::map.

By default std::map uses “operator <” as sorting criteria for keys. For default data types like int and std::string etc, operator < is available by default but for User defined classes operator < is not available by default.

Therefore, to use user defined class objects as keys in std::map we should have either,

  • Default sorting criteria i.e. operator < defined for our Class.
  • std::map should be assigned with an external sorting criteria i.e. comparator that can compare two objects of your user defined class.

Lets understand by example,

Overloading operator < for sorting of keys

Suppose our class is User that has id and name properties. To use this class as key in std::map we will overload operator <.

class User
{
    std::string m_id;
    std::string m_name;
public:
    User(std::string name, std::string id)
    :m_id(id), m_name(name)
    {}
    const std::string& getId() const {
        return m_id;
    }
    const std::string& getName() const {
        return m_name;
    }
    bool operator< (const User& userObj) const
    {
        if(userObj.m_id < this->m_id)
            return true;
    }
};

Now create a std::map with Class User as key and an integer as value bonded with each key.
Here in above User class operator < compares the id of each user object. So, map will contain only user objects with unique keys.

void example_1()
{
    std::map<User, int> m_UserInfoMap;

    m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "3"), 100) );
    m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "1"), 120) );
    m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.Z", "2"), 300) );

    std::map<User, int>::iterator it = m_UserInfoMap.begin();
    for(; it != m_UserInfoMap.end(); it++)
    {
        std::cout<<it->first.getName()<<" :: "<<it->second<<std::endl;
    }
}

 

Output :: Comparing by ID

Mr.X :: 100
Mr.Z :: 300
Mr.X :: 120

As we can see in output above, std::map can contain User object with unique Ids only therefore there are two entries for 2 Mr.X objects.

Now suppose we want to change the sorting criteria of keys i.e for User objects, instead of comparing by ID we want to compare them by name property.

To achieve this we have two options either change the definition of operator < or by using external sorting criteria i.e. comparators. For example,

Using Comparator for sorting of keys:

Comparator:

struct UserNameComparator
{
    bool operator()(const User & left, const User & right) const
    {
        return (left.getName() > right.getName());
    }
};
void example_2()
{
	std::map<User, int, UserNameComparator> m_UserInfoMap;

	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "3"), 100));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "1"), 120));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.Z", "2"), 300));

	std::map<User, int, UserNameComparator>::iterator it =
			m_UserInfoMap.begin();
	for (; it != m_UserInfoMap.end(); it++)
	{
		std::cout << it->first.getName() << " :: " << it->second << std::endl;
	}
}

Output :: Comparing by NAME

Mr.Z :: 300
Mr.X :: 100

As we can see in above output there is only 1 entry for name Mr.X because this time because we are comparing keys i.e User objects by name instead of Id.

Complete executable code is as follows,

#include <iostream>
#include <map>
#include <string>

class User
{
	std::string m_id;
	std::string m_name;
public:
	User(std::string name, std::string id) :
			m_id(id), m_name(name)
	{
	}
	const std::string& getId() const
	{
		return m_id;
	}
	const std::string& getName() const
	{
		return m_name;
	}
	bool operator<(const User& userObj) const
	{
		if (userObj.m_id < this->m_id)
			return true;
	}
};

void example_1()
{
	std::map<User, int> m_UserInfoMap;

	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "3"), 100));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "1"), 120));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.Z", "2"), 300));

	std::map<User, int>::iterator it = m_UserInfoMap.begin();
	for (; it != m_UserInfoMap.end(); it++)
	{
		std::cout << it->first.getName() << " :: " << it->second << std::endl;
	}
}
struct UserNameComparator
{
	bool operator()(const User &left, const User &right) const
	{
		return (left.getName() > right.getName());
	}
};

void example_2()
{
	std::map<User, int, UserNameComparator> m_UserInfoMap;

	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "3"), 100));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.X", "1"), 120));
	m_UserInfoMap.insert(std::make_pair<User, int>(User("Mr.Z", "2"), 300));

	std::map<User, int, UserNameComparator>::iterator it =
			m_UserInfoMap.begin();
	for (; it != m_UserInfoMap.end(); it++)
	{
		std::cout << it->first.getName() << " :: " << it->second << std::endl;
	}
}
int main()
{
	std::cout << "EXAMPLE 1 :: Comparing by ID" << std::endl;
	example_1();
	std::cout << "EXAMPLE 1 :: Comparing by NAME" << std::endl;
	example_2();
	return 0;
}

Output:

EXAMPLE 1 :: Comparing by ID
Mr.X :: 100
Mr.Z :: 300
Mr.X :: 120
EXAMPLE 1 :: Comparing by NAME
Mr.Z :: 300
Mr.X :: 100

 

1 thought on “C++ : Map Tutorial Part 3: Using User defined class objects as keys in std::map”

  1. Pingback: std::map Tutorial Part 1: Usage Detail with examples – thisPointer.com

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top