1

I have the algorithm for sort by last name, but I am having trouble figuring out how to sort by last name, then if two people have the same last name, sort by their first name.

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()

inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
Joe
  • 4,553
  • 9
  • 51
  • 57

11 Answers11

9

Why aren't you using std::sort? That's what it's 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 use the C++ std::string class as well. That's what it's for. And then you don't have to screw around with error-prone C string manipulation functions like stricmp.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • thanks, but I would like to see how to do it without std::sort – Joe May 14 '09 at 17:33
  • 6
    You apparently don't want to use C++ strings or vectors either. Makes me wonder why you don't just call it C. :) – jalf May 14 '09 at 17:47
5

First, compare the last names. If they are equal, then 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]
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
4

Firstly, use qsort or the proper C++ equivalent which takes a function which compares two objects.

The comparison should then 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 would probably use templates for the comparator function.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 1
    Since this is supposed to be C++, std::sort would be a much better choice – jalf May 14 '09 at 17:12
  • That's why I said equivalent - I'm a bit rusty on C++ ;-) – Alnitak May 14 '09 at 17:16
  • This is wrong. stricmp() returns a 3-valued integer (positive, zero , or negative), not a bool, so using || is completely wrong. The result of the || operator is always 0 or 1, NOT the value of one of the operands. – Adam Rosenfield May 14 '09 at 17:16
  • Your code is incorrect. return operator "||" will be evaluated as true(1) or false(0). You cannot use || here. – kcwu May 14 '09 at 17:19
  • OK, I've removed the || operator – Alnitak May 14 '09 at 17:22
3

You have to change your comparison. The basic algorithm is if friends[i] > friends[j] then swap them. So change your definition of ">" to include first name comparisons.

Something like the following ought to 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 do the last name comparison only once though (store it in a temp variable 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) instead.

lc.
  • 113,939
  • 20
  • 158
  • 187
2

In addition to the choice of building a comparison function that uses both names, you can sort on the fist names then sort on the last names, but you must take care to use a stable sort for the second pass. Might as well use it for the first too.

Good news: std::stable_sort is available and guaranteed to be, well, stable (thanks to Adam and libt for the correction). The plain std::sort and the c standard library qsort are not guaranteed to be stable, though some implementation might be. As Mark points out in the comments, the bubble sort you exhibit is already stable.


This is less efficient that the single-sort-with-a-custom-compare-function choice, but it makes it easy to let you user select multiple sorts at run-time (because you don't have to define every possible comparison function, or a mini-language).

Community
  • 1
  • 1
dmckee --- ex-moderator kitten
  • 98,632
  • 24
  • 142
  • 234
  • You might also point out that the Bubble sort shown in the question is a stable sort. Cut/Paste the loops and change the first loop to use firstName instead of lastName, and you're done. Slow, but done. – Mark Ransom May 14 '09 at 18:25
1

Please don't implement sorts by yourself - std::sort (in <algorithm>) does this job much better and much more efficient. (Except you just want to see how your algorithm works for experimental purpose)

You will have to specify a comparison function or better a functor anyway.

struct FriendComparer {
  bool operator () (const FRIEND& a, const FRIEND& b) {
      // Comparison code here (see previous posts)
  }
};

You can just invoke it like this:

std::sort(friendArray, friendArray + count, FriendComparer());
Dario
  • 48,658
  • 8
  • 97
  • 130
  • The original question looks an awful lot like a programming assignment, so I'm guessing that he's supposed to implement it himself :). – 17 of 26 May 14 '09 at 17:37
1

If you don't mind using boost.tuple (and replacing or at least modifying your existing implementation of Friend), there is an included comparison 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 the above should work.

Benoît
  • 16,798
  • 8
  • 46
  • 66
0

You can sort by last name and if last names are the same, sort them by the first names. Something like this (Bubble sort used):

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]);
    }
IS3NY
  • 43
  • 1
  • 5
0

Add the following:

else if (stricmp(friends[i].lastName, friends[j].lastName) == 0 &&
         stricmp(friends[i].firstName, friends[j].firstName) > 0) {
    temp = friends[i];    //swapping entire struct
    friends[i] = friends[j];
    friends[j] = temp;
}
KevinDTimm
  • 14,226
  • 3
  • 42
  • 60
0

Define a comparison function (or class as jalf suggested) and use the STL's 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);
}
Jon-Eric
  • 16,977
  • 9
  • 65
  • 97
0

you can use a concatenation of left aligned last name and first name as a sorting key

this is another point of view you're 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
}
jonny
  • 1,326
  • 9
  • 44
  • 62