Solved

# map sort question

Posted on 2005-04-28
382 Views
I map with the key that consists of 2 strings. I need to sort this map sometimes by the either 1st string or sometimes by second string. How do I do this?
Does it make sense to create a key class that has 2 string variables and do it with this class?
Here is an example of what I need, the second string part of my key is rating and my sort specifically needs to be
1) AAA
2) AA
3) A
4) BBB
5) BB
6) B

Is there a way to override map to allow me to sort specifically for the case above?
I need to somehow be able to override sort that map uses. Or is there a better way?

0
Question by:healingtao

LVL 86

Expert Comment

It IMHO would be easier and more effective to use two maps that have their own sort order and store refereces to the objects that you want them to hold.
0

LVL 15

Expert Comment

>  I need to sort this map sometimes by the either 1st string or sometimes by second string.

This is a bit ambiguous.  If you mean that you want to read entries from the map in key sequence and you want to be able to read it in two different sequences defined by two different keys, then I agree with jkr that you should use two maps as indexes to objects.

However, if you mean you want one sequence where the ordering takes two strings into account, then you are on the right track.  You should create a key class that contains two strings and define an operator < for that class.  You can make that operator do whatever you want, and the map will automatically use it to order objects by these keys.
0

LVL 5

Expert Comment

STL sort recieve functor(or function) as parameters, by which you can you can decide the sort algorithm.
bool sort_1st_string(const Key& k1, const Key& k2)
{
return k1.str1 < k2.str2;
}

bool sort_2nd_string(const Key& k1, const Key& k2)
{
return k1.str2 < k2.str2;
}

sort(map.begin(), map.end(), sort_1st_string);

sort(map.begin(), map.end(), sort_2nd_string);
0

LVL 15

Expert Comment

> sort(map.begin(), map.end(), sort_1st_string);

Sorry, this won't work.  Map iterators are birectional iterators, the sort function requires random-access iterators, and a bidirectional iterator can't take the place of a random-access iterator.
0

LVL 9

Expert Comment

>> Sorry, this won't work.  Map iterators are birectional iterators, the sort function requires random-access iterators, and a bidirectional iterator can't take the place of a random-access iterator.

what if overload the iterator using a wrapper or derived class.
0

Author Comment

I definitely want one sequence where the ordering takes two strings into account.
So you're saying I should create a key class and have logic like return key1.string1 < key2.string2 ??

but I want key1.string1 to have specific order like I mentioned
1) AAA
2) AA
3) A
4) BBB
5) BB
6) B
7) blank
and also if there is a blank for this string, I want it last as shown above.

How do I do this? because when I don't sort it at all by default the order is
1) blank
2) A
3) AA
4) AAA
5) B
6) BB
7) BBB

Thanks
0

Author Comment

I forgot to mention that the string previously mentioned is the second part of my key.
The first part should be sorted alphabetically. For example,

String1                                  String2
ABS                                   AAA
ABS                                   AA
ABS                                   A
ABS                                   BBB
ABS                                   BB
ABS                                   B
ABS TOTAL
AWL                                   AAA
AWL                                   AA
AWL                                   A
AWL                                       BBB
AWL                                       BB
AWL                                       B
AWL TOTAL

where string1 and string2 is one key

thanks
0

LVL 15

Expert Comment

>>> Sorry, this won't work.  Map iterators are birectional iterators, the sort function requires random-access iterators, and a bidirectional iterator can't take the place of a random-access iterator.

> what if overload the iterator using a wrapper or derived class.

That could be done, but it turns out that sorting in two different sequences is not what the questioner wants, so it's not very relevant at this point.

healingtao,

The usual approach to sorting by two keys is something like this pseudo code:

bool less(key1, key2)
{
if key1.part1 == key2.part1
return key1.part2 < key2.part2
else return key1.part1 < key2.part2
}

You only look at the secondary parts of the keys when the primary parts are equal.

In your case, you would have a more complex test for the secondary parts.

If you know that the empty secondary keys are always with total lines and total lines will be unique, you can ignore empty secondary keys in the sort function, because these lines will always be sorted by their primary keys.

If you do that, the sort logic for the secondary keys could first look at the initial characters of the two keys it is comparing.  If they are different, return a value based on comparing them.  If they are the same, return a value based on comparing their lengths.

--efn
0

LVL 15

Expert Comment

That should have been:

If they are different, return a value based on comparing the initial characters.  If they are the same, return a value based on comparing the lengths of the key strings.
0

Author Comment

Here is my code for Key class, can you please confirm that I didn't miss anything.
It seems to be working but I'm sure I missed a few things

#if !defined(RISKKEY__INCLUDED_)
#define RISKKEY__INCLUDED_
#include <string>

class RiskKey {
public:
RiskKey(){}
RiskKey(std::string strSector, std::string strCreditRating)
{
m_strSector = strSector;
m_strCreditRating = strCreditRating;
}
virtual ~RiskKey(){}
inline const std::string& getSector() const { return m_strSector;}
inline void setSector(std::string& strSector) {m_strSector = strSector;}
inline  const std::string& getCreditRating() const { return m_strCreditRating;}
inline void setCreditRating(std::string& strCreditRating) {m_strCreditRating = strCreditRating;}

friend inline bool operator<(const RiskKey &key1, const RiskKey &key2)
{
if (key1.getSector() != key2.getSector())
return (key1.getSector() < key2.getSector());

if (key1.getCreditRating() != key2.getCreditRating())
{
if (key1.getCreditRating().substr(0,1) == key2.getCreditRating().substr(0,1))
{
if (key1.getCreditRating().length() > key2.getCreditRating().length())
return true;
else
return false;
}
else
{
return (key1.getCreditRating() < key2.getCreditRating());
}

}
}

private:
std::string                  m_strSector;
std::string                  m_strCreditRating;
};

#endif
0

LVL 15

Expert Comment

The only significant problem I see is that the comparison function doesn't deal with the case where the sectors are equal and the credit ratings are equal.  If you know there will not be any such records, I guess that's OK.
0

Author Comment

efn,

In the case they are equal, do I return true or false?
0

LVL 15

Accepted Solution

In that case, it is not true that key1 < key2, so return false.
0

## Featured Post

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article locâ€¦
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticallâ€¦
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.