Simultaneously sorting Vector elements alphabetically and ignoring integers in sort.

I am attempting to sort a vector of string elements in alphabetical order, outputting them using an iterator. The Vector elements are in the following format:

PHYS 30485 Physics of lasers
(This is an example element, but every element follows this format) You have "PHYS" followed by a whitespace then 5 digits, followed by a second whitespace before a string. Each of these elements are entered in on one line. Nothing fancy here.)

The issue here is when I write my sort line it automatically sorts by integer order (using the 5 digit number), whereas I want it to sort using the string after the 5 digits.
The code I'm using to sort each element in the array is as follows:
1
2
3
4
5
vector<string>::iterator namestart{ matchingcourses[0] }, nameend{ matchingcourses.end() };
		vector<string>::iterator j2;
		sort(namestart., nameend);
		for (j2 = namestart; j2 < nameend; j2++, compareFunction)
			cout << *j2 << endl;

(The "matchingcourses" is a vector that contains multiple of the elements in the example format previously stated)
So my question summarised is simply: How do I make this code sort alphabetically by name, rather than defaulting to the ascending order of the integers, whilst still outputting the full element in the specified format?
Last edited on
compareFunction is very strange. One would have thought it would be used during the sort. If it was the following then it will "work". But the name must always begin at position 11.

1
2
3
bool compareFunction(const std::string& a, const std::string& b) {
    return a.substr(11) < b.substr(11);
}


It would be better to read the data into separate fields of a struct and sort those.
Last edited on
std::sort has a 3 parameter version (or it's that the 3rd parameter has a default comparison of less than).

What you need is a custom comparison function object.

If I assume the format always has the same position, such that the string of interest always starts at position 11 (as your example does above), then a comparison function object might be as simple as:

1
2
3
4
inline bool SpecComp( const std::string & s1, const std::string & s2 )
{
 return s1.substr( 11 ) < s2.substr( 11 );
}



With such a function defined, sort would be called with:

sort( namestart, nameend, SpecComp );

This would sort by the string starting at position 11.

If the position "floats" in that code sequence, the comparison function may become more complicated, where tokens are used to find the position, and using that position for the call to substr.

Other variations could apply, as you might invent going forward.

Might help if you posted code that compiles that reproduces your issue. For example, I don't know why you have "compareFunction" inside a for loop, that looks like it belongs inside your call to sort.

zakinithos wrote:
How do I make this code sort alphabetically by name, rather than defaulting to the ascending order of the integers, whilst still outputting the full element in the specified format?
There's multiple ways to solve this, and which solution is best depends on the usage of the program.

One way, which I think is what you were are going for, is to keep it is a vector<string>, but give it a custom compare function. Because you have such rigid constraints on the format of the input, it is rather easy to just get the substring of the latter part of the string.

Here's an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Example program
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>

/// Giving a custom function to std::sort makes it use it as a "less than" function.
bool compareByPhysClassTitle(const std::string& class_desc1, const std::string& class_desc2)
{
    const int substr_start_index = 4 + 1 + 5 + 1;
    return class_desc1.substr(substr_start_index) < class_desc2.substr(substr_start_index);
}

int main()
{
    std::vector<std::string> uni_classes {
        "PHYS 38383 Title of class",
        "PHYS 30485 Physics of lasers",
        "PHYS 15000 Roentgen, I heard it's the equivalent of a chest x-ray",
        "PHYS 00001 RBMK Reactors don't explode",
    };
  
    std::sort(uni_classes.begin(), uni_classes.end(), compareByPhysClassTitle);
    
    for (const auto& s : uni_classes)
    {
        std::cout << s << '\n';   
    }
}


(Seems I should have refreshed the page before posting, whoops.)

In the future, if the format of the string gets quite complex, you might want to consider breaking it into a class/struct, and have the class number or description be its own variable.
Last edited on
(Seems I should have refreshed the page before posting, whoops.)


Great minds.....

In the future, if the format of the string gets quite complex, you might want to consider breaking it into a class/struct, and have the class number or description be its own variable.


This is a key point.
Great minds...

...and fools. Though I clearly have you beat in that domain. :-)

An example using a struct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct Course {
    std::string abbrev;
    int code;
    std::string descrip;

    Course(std::string abbrev, int code, std::string descrip)
        : abbrev(abbrev), code(code), descrip(descrip) {}
};

std::ostream& operator<<(std::ostream& os, const Course& course) {
    return os << std::left  << std::setw(4) << course.abbrev << ' '
              << std::right << std::setw(5) << course.code   << ' '
              << course.descrip;
}

int main() {

    std::istringstream in(
        "PHYS 30485 Physics of lasers\n"
        "PHYS 12345 Lasers of physics\n"
        "PHYS 34821 Manners\n"
        "PHYS 47298 Basic Doomsday Machines\n");
    //std::istream& in = std::cin;

    std::vector<Course> courses;

    std::string line;
    while (std::getline(in, line)) {
        std::istringstream ss(line);
        std::string abbrev, descrip;
        int code;
        ss >> abbrev >> code;
        std::getline(ss >> std::ws, descrip);
        courses.push_back(Course(abbrev, code, descrip));
    }
    
    std::sort(courses.begin(), courses.end(),
        [](const Course& a, const Course& b){return a.descrip < b.descrip;});

    for (const auto& x: courses)
        std::cout << x << '\n';
}

Last edited on
...and fools.


...can we agree just not to talk about those times?
My lips are sealed.
I hope the llama survived.
Topic archived. No new replies allowed.