Sorting a vector

Dec 7, 2011 at 3:52pm
Hi all,

I'm having some trouble with sorting a vector that I have. That is to say, I've managed to sort it using std::sort, but I actually need it to be sorted in a more human-friendly fashion.

Basically, I have strings in my vector that are similar to the following:
IDS_STRING3
IDS_STRING10
IDS_STRING1
IDS_STRING20
etc.

And when using sort they come out as follows:
IDS_STRING1
IDS_STRING10
IDS_STRING20
IDS_STRING3

I understand why this happens, but it's not what I want. I need it so the output would be IDS_STRING1, 3, 10, 20.

Here's some of my code (if it's helpful):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
freopen("CHoutput.txt", "a", stdout); //redirects console output to file
					printf("%s", "IDS_STRING" + ResourceString->Value + "\t" + Translation->Text + "\n");
					std::fstream filetestsort("CHoutput.txt");
					std::vector<std::string> rows;
					freopen("Chinese.txt", "w", stdout);

					while(!filetestsort.eof())
					{
						std::string line;
						std::getline(filetestsort, line);
						rows.push_back(line);
					}

					std::sort(rows.begin(),rows.end()); // sorts lines --> need to make it so it sorts naturally
					unique(rows.begin(),rows.end());	//gets rid of any duplicate lines

					std::vector<std::string>::iterator iterator = rows.begin();
					for(; iterator != rows.end(); ++iterator)
						cout << *iterator << std::endl;
						getchar();
					fclose(stdout);


If anyone could shed any light on how to sort the vector in a more natural way, it would be GREATLY appreciated :)

Thanks in advance.
Dec 7, 2011 at 4:02pm
You can make completely different sorting rules by using predicates.

A predicate is a function that takes two elements and returns a boolean. For example, if you have a list of 10 students (numbered 1->10) that each have a grade (student 1's grade is located in grades[0], student10's grade is located in grades[9]), you could sort it this way:

1
2
3
4
5
6
double grades[10] = {4.8, 5.2, 1.6, 9.7, 5.0, 6.7, 1.9, 5.1, 7.1, 8.5};
int students[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
bool gradePred(const int i, const int j) {
    grades[i-1] > grades[j-1];
}
std::sort(grades, grades+10, gradePred);

The sad thing is that your predicate function can't take any other parameters and you can't (?) define local functions, so you'll have to find a way to keep your predicate generic enough to keep it global.

In your case, your predicate will need to be able to find the numerical part of string and compare those.

[edit]

Actually, in this case, it might be sufficient to sort twice: once on number (like now) and once on string length. I think the std::sort guarantees a stable sort [i.e. the order of 'equal' elements is maintained]. As a result, the second sort (on str length) will put 100 after 10, but will keep it before 200 because they have the same str length but 200 was after 100 after the first sort.

[edit2]

Just checked it and it works.
Last edited on Dec 7, 2011 at 4:13pm
Dec 7, 2011 at 4:30pm
Hi Gaminic,

Thanks, that's helped me greatly :)

Now I face another problem..

I was using those IDS_STRING's as a test for my code. I will actually end up with strings in the following format:

IDS_STRING3 "Actuator"
IDS_STRING10 "On/Off"
IDS_STRING1 "Service"
IDS_STRING2 "some other sentence..."
etc.

So each string has the ids_string identifier and then a command. Now, if I sort this by length my results could end up incorrect. Is there a way I can split the id apart from the command and then sort?

Sorry for making it more complicated :S

Thanks again
Dec 7, 2011 at 7:02pm
Those ID's are just names which don't exist anymore at runtime. I honestly don't get why you'd want to sort them by the name you've given them - if you want them to be in a particular order, just insert them in that order. Other than that you COULD also define an own predicate, specifically one that maps each of your strings to a corresponding ID nr:

1
2
3
4
5
map<std::string, int> stringIDMap;
bool stringCompare(const std::string& left, const std::string& right) 
{
      return *(stringIDMap.find(left))<*(stringIDMap.find(right));
} 


(see http://www.cplusplus.com/reference/stl/map/)

You could use that for comparisons (insert the strings and the corresponding ID's into the map first, of course).

A cleaner way to do this would be with a functor (avoids the global variable), or, even better, a lambda if your compiler supports them. Other than that, you don't have too many choices here.

Oh, and by the way - why do you mix C and C++ that much? IMHO: Either use C++ I/O, OR use C I/O. Not both.
Last edited on Dec 7, 2011 at 7:09pm
Dec 8, 2011 at 3:13pm
Thanks for the reply.

Sorry, I should have explained what i'm trying to achieve in more detail.

I basically have an application that is used for translating files into other languages (i.e. chinese, romanian etc). The files that are being translated are for a program that one of our customers has been issued with by us previously and contain all commands, menu items, messages etc.

The application will be used by a translator where they select a resource id number (the IDS_STRING value) and the english string for that id is displayed. They then type out the translation and it gets stored in the equivalent language file. This all sounds straightforward so far..

However, the translated files may already contain a translation that has been inputted previously. if this is the case then that translation must be displayed to show the translator that someone has already done it. BUT the translator has the ability to correct any incorrect translations that have been inserted at a prior moment in time.
Also, the translations can be written in any order (i.e. they may choose to start from the last id and work their way backwards).
These are the reasons for needing the IDS_STRINGS to be in order.

So, it looks like defining my own predicate is the way to go then? I'll have a deeper look into that now.

And my apologies, I don't usually work in c++ so that's why it's a bit mixed up, but i'll work on sorting that out :)

Thanks again
Dec 8, 2011 at 3:52pm
I'm still not sure why you require sorting. As long as they have the ID, they can look up any IDS/command pair. Let the data structure determine how to organize them; as long as it can store and access them easily, why would you care about the order they're saved in?
Dec 8, 2011 at 4:00pm
Maybe it's just the technique i'm using that's wrong then, but the files are actually text files (they get converted to rc files at a later stage), so at the moment when the user selects a resource number from a numeric up and down box, the ids_string is displayed from pulling the equivalent line from the text file (so if the user selects resource id 5, the 5th line is read from the text file). That's why I require them to be stored in the correct order.

I'd happily use another way of doing it, but after researching I couldn't find anything, hence using my own make-shift (and perhaps shoddy) technique.
Topic archived. No new replies allowed.