tags:

views:

517

answers:

10

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

+5  A: 

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
+4  A: 

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
Since this is supposed to be C++, std::sort would be a much better choice
jalf
That's why I said equivalent - I'm a bit rusty on C++ ;-)
Alnitak
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
Your code is incorrect. return operator "||" will be evaluated as true(1) or false(0). You cannot use || here.
kcwu
doh - too use to Perl's way of doing things!!! my bad...
Alnitak
OK, I've removed the || operator
Alnitak
A: 

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
This unnecessarily compares the last names an extra time. Just compare them once and cache the result.
Adam Rosenfield
#1 - you're correct (see #2)#2 - he's implementing a bubble sort
KevinDTimm
+3  A: 

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
+1  A: 

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
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
+9  A: 

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
thanks, but I would like to see how to do it without std::sort
Joe
You apparently don't want to use C++ strings or vectors either. Makes me wonder why you don't just call it C. :)
jalf
+2  A: 

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

dmckee
std::list<T>::sort() is stable, but the general-purpose std::sort is NOT stable. Fortunately, there is also the function std::stable_sort(), which as its name implies, is stable.
Adam Rosenfield
Ah. Thanks. Will edit.
dmckee
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
A: 

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
+1  A: 

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
A: 

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