Sort structure by last name, then name
I have an algorithm for sorting by last name, but I am having a hard time figuring out how to sort by first name, then if two people have the same first name, sort them by first.
void sortLastName(FRIEND friends[ARRAY_MAX], int& count) {
FRIEND temp;
for(int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (stricmp(friends[i].lastName, friends[j].lastName) > 0) {
temp = friends[i]; //swapping entire struct
friends[i] = friends[j];
friends[j] = temp;
}
}
}
}
=== EDIT ====================
I don't want to use STD sort()
a source to share
Why don't you use std::sort
? What is this for:
struct NameComparer {
bool operator()(const FRIEND& lhs, const FRIEND& rhs){
int compareResult = stricmp(lhs.lastName, rhs.lastName);
if(compareResult == 0){
compareResult = stricmp(lhs.firstName, rhs.firstName);
}
return compareResult < 0;
}
};
std::sort(friends, friends + ARRAY_MAX, NameComparer());
Of course, you really should be using the C ++ class std::string
. What is this for. And then you don't need to curl with error C manipulation functions like stricmp
.
a source to share
Compare the last names first. If they are equal, compare the first names:
int compareResult = stricmp(friends[i].lastName, friends[j].lastName);
if(compareResult == 0)
compareResult = stricmp(friends[i].firstName, friends[j].firstName);
if(compareResult < 0)
// swap friends[i] and friends[j]
a source to share
First, use qsort
or the corresponding C ++ equivalent, which takes a function that compares two objects.
Then the comparison should be trivial:
int compare_by_name(const FRIEND& f1, const FRIEND& f2)
{
int last_name_matches = strcmpi(f1.lastName, f2.lastName);
return (last_name_matches != 0) ? last_name_matches :
strcmpi(f1.firstName, f2.firstName) ;
}
NB: A real C ++ implementation will probably use templates for the comparator function.
a source to share
You must change your comparison. The basic algorithm is if friends [i]> friends [j] then change them. So change your ">" definition to include name comparisons.
Something like the following should do:
if (stricmp(friends[i].lastName, friends[j].lastName) > 0 ||
(stricmp(friends[i].lastName, friends[j].lastName) == 0 &&
stricmp(friends[i].firstName, friends[j].firstName) > 0))
You might want to only compare names once (store it in temp instead of comparing twice), but the idea is the same.
Note that the "best" way to do this might be to provide comparison functions in the FRIEND class. Then you can use if(friends[i].CompareTo(friends[j]) > 0)
.
a source to share
In addition to choosing to build a comparison function that uses both names, you can sort by fist names and then sort the names, but you must take care to use a stable sort for the second pass. You can also use it for the first one too.
Good news: std::stable_sort
available and guaranteed to be stable (thanks to Adam and Libt for fixing it). The regular std::sort
and standard c libraries are qsort
not guaranteed to be stable, although there may be some implementation. As Mark points out in the comments, the bubble sort you are showing is already stable.
This is less efficient than selecting one-by-one-sort using the custom-compare-function, but makes it easy to select a user at runtime across multiple views (since you don't have to define every possible comparison function or minilanguage).
a source to share
Don't do the sort yourself - std :: sort (in <algorithm>
) does the job much better and much more efficiently. (Except you just want to see how your algorithm works for an experimental purpose)
In any case, you will need to specify a comparison function or better a functor.
struct FriendComparer {
bool operator () (const FRIEND& a, const FRIEND& b) {
// Comparison code here (see previous posts)
}
};
You can simply call it like this:
std::sort(friendArray, friendArray + count, FriendComparer());
a source to share
If you don't mind using boost.tuple (and replacing or at least modifying an existing Friend implementation) there is a comparison included function.
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
typedef boost::tuple<std::string, std::string> Friend;
Friend f1, f2;
bool compareFriends = f1 < f2;
All of the above should work.
a source to share
Define a compare function (or class as suggested by jalf) and use STL std :: sort ():
bool compareFriends(FRIEND const & lhs, FRIEND const & rhs)
{
int const resultLast = stricmp(lhs.lastName, rhs.lastName);
if(resultLast == 0)
{
return stricmp(lhs.firstName, rhs.firstName) < 0;
}
else
{
return resultLast < 0
}
}
void sortLastName(FRIEND friends[ARRAY_MAX], int& count)
{
std::sort(friends, friends + count, &compareFriends);
}
a source to share
you can use the concatenation of the left aligned last name and first name as the sort key
this is another point of view you are looking for i think :)
string key(const FRIEND& aFriend)
{
const int INITIALS_MAX_LENGTH = 200; // assume the worst
string firstNameKeyPart = string(INITIALS_MAX_LENGTH, ' ');
string lastNameKeyPart = string(INITIALS_MAX_LENGTH, ' ');
firstNameKeyPart.replace(0, 0, aFriend.firstName);
lastNameKeyPart.replace(0, 0, aFriend.lastName);
return lastNameKeyPart + firstNameKeyPart;
}
//...
if ( key(friends[i]) > key(friends[j]) )
{
//swap
}
a source to share
You can sort by last name and if the last names match, sort them by first name. Something like this (using Bubble sort):
for (int i = 1; i < dictionary.size(); ++i)
for (int j = 0; j < dictionary.size()-1; ++j) {
if (dictionary[i].Last < dictionary[j].Last)
swap(dictionary[i], dictionary[j]);
}
for (int i = 1; i < dictionary.size(); ++i)
for (int j = 0; j < dictionary.size() - 1; ++j) {
if (dictionary[i].Last == dictionary[j].Last && dictionary[i].First < dictionary[j].First)
swap(dictionary[i], dictionary[j]);
}
a source to share