3

My program sorts alphabetically by last name and places the first name beside it, however I want it to also sort by first name as well. How would I go about this?

    // Before sort
    for (int i = 0; i < NUM_PEOPLE; i++) {
       cout << first_names[i] << "\t" << last_names[i] << endl;
    }

    string temp;

    for (int pass = 0; pass < NUM_PEOPLE - 1; pass++)
        for (int i = 0; i < NUM_PEOPLE - 1; i++)
            if (last_names[i] > last_names[i + 1]) {
            temp = last_names[i];
            last_names[i] = last_names[i + 1];
            last_names[i + 1] = temp;
            temp = first_names[i];
            first_names[i] = first_names[i + 1];
            first_names[i + 1] = temp;
        }

    cout << "After sort:" << endl;

    for (int i = 0; i < NUM_PEOPLE; i++)
        cout << last_names[i] << "\t" << first_names[i]<< endl;
some user
  • 1,693
  • 2
  • 14
  • 31
shiloot
  • 31
  • 2
  • Do you need to keep first and last names in separate containers? This gets much simpler if you have a type composes them together (e.g., `struct Person {std::string first_name; std::string last_name; };`). – Stephen Newell May 21 '20 at 04:44
  • 2
    The better way to do this is *not* to sort the names, but sort an array of indices. If you had more than 2 arrays, say 10 arrays, and you want to keep them in sync, would you want to write 30+ lines of swapping code? Of course not. – PaulMcKenzie May 21 '20 at 04:48

6 Answers6

3

If you have no choice but to have the data in separate arrays or containers, the better way to do these types of sorts is to use an additional array of index numbers, and sort the index numbers.

The reason why you would want to do things this way is that if you had more than 2 arrays, it becomes more difficult to write 3 lines of swapping code per array that needs to be swapped. It would be much easier to swap just one array, regardless of the number of arrays you may have to keep in sync.

Here is an example:

//...

// Create an array of indices, starting from 0.
int index[NUM_PEOPLE];
for (int i = 0; i < NUM_PEOPLE; ++i)
   index[i] = i;

int temp;

for (int pass = 0; pass < NUM_PEOPLE - 1; pass++)
{   
    for (int i = 0; i < NUM_PEOPLE - 1; i++)
    {
        if (last_names[index[i]] > last_names[index[i + 1]]) 
        {
            temp = index[i];
            index[i] = index[i + 1];
            index[i + 1] = temp;
        }
    }
}
cout << "After sort:" << endl;

for (int i = 0; i < NUM_PEOPLE; i++)
    cout << last_names[index[i]] << "\t" << first_names[index[i]]<< endl;

Note that there is only one item swapped, and that is the index array -- the first_name and last_name arrays are not changed whatsoever. Note that when accessing the arrays in a sorted manner (note the output at the end), we use the index array as the index into each individual array.

Here is a complete example.


Here is a solution using std::sort that uses the same technique explained above:

#include <iostream>
#include <string>
#include <algorithm>

const int NUM_PEOPLE = 5;

int main()
{
    std::string first_names[NUM_PEOPLE] = { "Joe", "Peter", "Andy", "Anny", "Drake" };
    std::string last_names[NUM_PEOPLE] = { "Johnson", "Parker", "Sanchez", "Nguyen", "Bell" };

    // Before sort
    std::cout << "\nBefore sort:\n" << std::endl;
    for (int i = 0; i < NUM_PEOPLE; i++) {
        std::cout << first_names[i] << "\t" << last_names[i] << std::endl;
    }
    int index[NUM_PEOPLE];
    for (int i = 0; i < NUM_PEOPLE; ++i)
        index[i] = i;

    std::sort(index, index + NUM_PEOPLE, [&](int n1, int n2) 
    { return last_names[index[n1]] < last_names[index[n2]]; });

    std::cout << "\nAfter sort:\n" << std::endl;

    for (int i = 0; i < NUM_PEOPLE; i++)
        std::cout << last_names[index[i]] << "\t" << first_names[index[i]] << std::endl;
}

Output:

Before sort:

Joe Johnson
Peter   Parker
Andy    Sanchez
Anny    Nguyen
Drake   Bell

After sort:

Bell    Drake
Johnson Joe
Parker  Peter
Nguyen  Anny
Sanchez Andy
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
1

I tried to keep it as simple as possible hope you find this useful.
I have also added Input from user option.
I like to keep my main function as clean as possible :D.

Old Program:-

// Before sort
    for (int i = 0; i < NUM_PEOPLE; i++) {
       cout << first_names[i] << "\t" << last_names[i] << endl;
    }

    string temp;

    for (int pass = 0; pass < NUM_PEOPLE - 1; pass++)
        for (int i = 0; i < NUM_PEOPLE - 1; i++)
            if (last_names[i] > last_names[i + 1]) {
            temp = last_names[i];
            last_names[i] = last_names[i + 1];
            last_names[i + 1] = temp;
            temp = first_names[i];
            first_names[i] = first_names[i + 1];
            first_names[i + 1] = temp;
        }

    cout << "After sort:" << endl;

    for (int i = 0; i < NUM_PEOPLE; i++)
        cout << last_names[i] << "\t" << first_names[i]<< endl;

New Program:-

#include<iostream>
#include<string>
using namespace std;

const int NAMES_SIZE = 5;
void names_Function();
void input_Function(string[]);
void print_Function(string[]);

int main() {
    names_Function();
    return EXIT_SUCCESS;
}

void names_Function() {
    string names[NAMES_SIZE] = { {"Joe Johnson"}, {"Peter Parker"}, {"Andy Sanchez"}, {"Anny Nguyen"}, {"Drake Bell"} };
    //input_Function(names);
    cout << "Before Sorting:-\n\n";
    print_Function(names);
    for (int i = 0; i < NAMES_SIZE; i++) {
        for (int j = i + 1; j < NAMES_SIZE; j++) {
            if (names[i][0] > names[j][0]) {
                swap(names[i], names[j]);
            }
        }
    }
    cout << "After  Sorting:-\n\n";
    print_Function(names);
}

void input_Function(string names[]) {
    for (int i = 0; i < NAMES_SIZE; i++) {
        cout << "Enter Name of Person # " << i + 1 << " : ";
        getline(cin, names[i]);
    }
}

void print_Function(string names[]) {
    for (int i = 0; i < NAMES_SIZE; i++) {
        cout << names[i] << endl;
    } cout << endl;
}

Output:-

Before Sorting:-

Joe Johnson
Peter Parker
Andy Sanchez
Anny Nguyen
Drake Bell

After  Sorting:-

Andy Sanchez
Anny Nguyen
Drake Bell
Joe Johnson
Peter Parker

Press any key to continue . . . _
Mehroz Mustafa
  • 222
  • 2
  • 14
1

Seems like none of the other answers so far are sorting by first name. If you're alright with using STL containers and the std::sort algorithm, consider storing a vector of firstName, lastName pairs and providing your custom comparison function to consider the first and last name:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

int main()
{
    vector< pair<string, string> > names;
    string firstName, lastName;
    while( cin >> firstName >> lastName ) {
        names.emplace_back(firstName, lastName);
    }
    sort( names.begin(), names.end(), [](auto const& a, auto const& b) {
        if( a.second < b.second ) return true;
        if( a.second == b.second ) return a.first < b.first;
        return false;
    } );
    for( auto const& name : names ) {
        cout << name.first << " " << name.second << "\n";
    }
    return 0;
}
Nic
  • 640
  • 7
  • 9
0

This should give you your desired result. I've done the inner loop a bit differently and the comparison slightly different. The idea is to have the parent loop, your pass count, loop up until the last element which you did correctly.

The inner loop should look ahead of your current position that the parent loop is at to give every element in the array a chance at being compared. You want to ensure you swap the entire row respectively when your swap condition is satisfied.

                // Preserve our original value before reassigning.
                temp = last_names[i];
                // Reassign element at the position ahead of us.
                last_names[i] = last_names[pass];
                // Update the element at our current position stored in temp.
                last_names[pass] = temp;
                // Swap first names same as last names.
                temp = first_names[i];
                first_names[i] = first_names[pass];
                first_names[pass] = temp;

Full code snippet:

int main() {

    // Before sort
    for (int i = 0; i < NUM_PEOPLE; i++) {
        cout << first_names[i] << "\t" << last_names[i] << endl;
    }

    string temp;

    // Iterate up until the last element.
    for (int pass = 0; pass < NUM_PEOPLE - 1; pass++) {
        // Look one ahead of our current position for comparison purposes.
        for (int i = pass + 1; i < NUM_PEOPLE; i++) {
            // Compare if the last name ahead of us is smaller than our current.
            if (last_names[i] < last_names[pass]) {
                // Swap last names.
                temp = last_names[i];
                last_names[i] = last_names[pass];
                last_names[pass] = temp;
                // Swap first names.
                temp = first_names[i];
                first_names[i] = first_names[pass];
                first_names[pass] = temp;
            }
        }
    }

    cout << "After sort:" << endl;

    for (int i = 0; i < NUM_PEOPLE; i++)
        cout << last_names[i] << "\t" << first_names[i]<< endl;

    return 0;
}

Output:

Before sort:
Joe Johnson
Peter   Parker
Andy    Sanchez
Anny    Nguyen
Drake   Bell
After sort:
Bell    Drake
Johnson Joe
Nguyen  Anny
Parker  Peter
Sanchez Andy```
heaven
  • 23
  • 7
0

I would recommend using STL containers. It's a cleaner and more readable approach. Here's a full example program which you can adapt to your needs.

#include <vector>
#include <iostream>
#include <tuple>
#include <algorithm>

void print(std::vector<std::tuple<std::string, std::string>>& v)
{
    using namespace std;
     for (int i = 0; i < v.size(); i++)  
        cout << get<0>(v[i]) << " " 
             << get<1>(v[i]) << " " << endl;
}

bool compSecondElem(const std::tuple<std::string, std::string>& a,  
               const std::tuple<std::string, std::string>& b) 
{ 
    using namespace std;
    return (get<1>(a) < get<1>(b)); 
} 

int main()
{
    using namespace std;
    vector<tuple<string, string>> names;

    // add to vector as (lastname, firstname)
    names.push_back(make_tuple("Holmes", "Sherlock"));
    names.push_back(make_tuple("Holmes", "Elizabeth"));
    names.push_back(make_tuple("Liz", "Taylor"));

    sort(names.begin(), names.end());  //by default this sorts by 0th element
    sort(names.begin(), names.end(), compSecondElem); // use compSecondElem() to sort
    print(names);
}

Output:

Holmes Elizabeth 
Holmes Sherlock 
Liz Taylor 
Roy2511
  • 938
  • 1
  • 5
  • 22
0

I think the other answers are good solutions to the problem but they don't quite answer the problem for your exact code and try to completely rework it. Hopefully this is able to better address your exact question with the fewest changes possible.

I am a bit unclear on weather or not you want one that sorts by last name then if needed first name or two separate sorts (one for first name and one for last) so I sorted it twice with each interpretation respectively.

For the first interpretation I added a second if statement that checked if the last names were the same and then sorted the first names if the last ones were already in order.

For the second interpretation I just changed it to sort by first names instead of last as your code already swapped both names at the same time.

#include <iostream>
#include <string>
using namespace std;



int main(){
        const int NUM_PEOPLE = 7;

        //sorting lastnames by length then first
        cout << endl << "Version that sorts last then first names:" << endl;
        string first_names[NUM_PEOPLE] = {"North","Franklin","Kanye","Ye","Bob","Tim","Arnold"};
        string last_names[NUM_PEOPLE] = {"West","Smith","Johnson","West","Ash","Wolf","Kelly"};
        for (int i = 0; i < NUM_PEOPLE; i++) {
                cout << first_names[i] << "\t" << last_names[i] << endl;
        }

        string temp;
        for (int pass = 0; pass < NUM_PEOPLE - 1; pass++){
                for (int i = 0; i < NUM_PEOPLE - 1; i++){
                        if (last_names[i] > last_names[i + 1]) {
                                temp = last_names[i];
                                last_names[i] = last_names[i + 1];
                                last_names[i + 1] = temp;
                                temp = first_names[i];
                                first_names[i] = first_names[i + 1];
                                first_names[i + 1] = temp;
                        }
                        else if(last_names[i] == last_names[i + 1] 
                                        && first_names[i] > first_names[i + 1]){
                                temp = first_names[i];
                                first_names[i] = first_names[i + 1];
                                first_names[i + 1] = temp;
                        }
                }
        }

        cout << "After sort:" << endl;
        for (int i = 0; i < NUM_PEOPLE; i++)
                cout << last_names[i] << "\t" << first_names[i]<< endl;

        //sorting by first name length only
        cout << endl << "Version that only sorts by first name length:" << endl;
        string first_names2[NUM_PEOPLE] = {"North","Franklin","Kanye","Ye","Bob","Tim","Arnold"};
        string last_names2[NUM_PEOPLE] = {"West","Smith","Johnson","West","Ash","Wolf","Kelly"};
        for (int i = 0; i < NUM_PEOPLE; i++) {
                cout << first_names[i] << "\t" << last_names[i] << endl;
        }

        for (int pass = 0; pass < NUM_PEOPLE - 1; pass++){
                for (int i = 0; i < NUM_PEOPLE - 1; i++){
                        if (first_names2[i] > first_names2[i + 1]) {
                                temp = last_names2[i];
                                last_names2[i] = last_names2[i + 1];
                                last_names2[i + 1] = temp;
                                temp = first_names2[i];
                                first_names2[i] = first_names2[i + 1];
                                first_names2[i + 1] = temp;
                        }
                }
        }

        cout << "After sort:" << endl;

        for (int i = 0; i < NUM_PEOPLE; i++)
                cout << last_names2[i] << "\t" << first_names2[i]<< endl;
        return 0;
}

Output:

Version that sorts last then first names:
North   West
Franklin        Smith
Kanye   Johnson
Ye      West
Bob     Ash
Tim     Wolf
Arnold  Kelly
After sort:
Ash     Bob
Johnson Kanye
Kelly   Arnold
Smith   Franklin
West    North
West    Ye
Wolf    Tim

Version that only sorts by first name length:
Bob     Ash
Kanye   Johnson
Arnold  Kelly
Franklin        Smith
North   West
Ye      West
Tim     Wolf
After sort:
Kelly   Arnold
Ash     Bob
Smith   Franklin
Johnson Kanye
West    North
Wolf    Tim
West    Ye

If this is what you are looking for I would still recommend looking at the other answers as some of them make improvements on the efficiency of your sort and do things in a more standard c++ way.