std::sort provides a simple way of sorting container values. Many times the provided std::greater or std::less comparisons can be used without having to write a custom comparison function. However, in your case, you want to compare string values based on the sum the the ASCII values of each character in the string. When a custom compare function is needed, you simply write a comparison function to pass to std::sort
. The prototype is:
bool cmp(const Type1 &a, const Type2 &b);
Your function simply needs to iterate over each value in each string saving the sum of the ASCII values and then you return true
if a
sorts before b
, or false
otherwise to tell std::sort
whether to swap a
and b
.
std::accumulate allows you to quickly sum the values within a container (int
, double
, char
, etc..) In the case of your std::string
it can be treated as a std::vector<char>
and the ASCII value of each character summed. Say into suma
and sumb
. Then you can simply return the comparison of suma < sumb
.
Putting that together, you can write your custom comparison function as:
bool compare (const std::string& a, const std::string& b)
{
int suma = std::accumulate(a.begin(), a.end(), 0), /* sum values in a */
sumb = std::accumulate(b.begin(), b.end(), 0); /* sum values in b */
return suma < sumb; /* return comparison */
}
(note: to guarantee that suma
and sumb
do not suffer integer overflow, you must limit the number of characters in your string to 17,043,521
(or less) to cover the worst case scenario of a string with all characters '~'
, e.g. ASCII 126 -- that should not be a problem with any of your normal input. If it is, change suma
and sumb
from int
to a larger size like uint64_t
, etc..)
Then it is just a matter of reading the first value in input, and looping that many times reading strings input into a std::vector<std::string>
. You then call std::sort
on your vector of strings providing your custom compare function as the 3rd argument and then output the sorted results, e.g.
int main (void) {
int n = 0; /* int for 1st value input */
std::vector<std::string> vs {}; /* storage for each string */
if (!(std::cin >> n) || n < 0) { /* read n from input */
std::cerr << "invalid format - n.\n";
return 1;
} /* clear to end of line (removing \n) */
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
while (n--) { /* loop n times */
std::string tmp; /* temporary string */
if (!getline (std::cin, tmp)) { /* read/validate input into tmp */
std::cerr << "error: failed read of string.\n";
return 1;
}
vs.push_back(tmp); /* add to vector */
}
std::sort (vs.begin(), vs.end(), compare); /* sort w/compare function */
for (auto& s : vs) /* loop over sorted strings */
std::cout << s << '\n'; /* output each string */
}
Adding the required headers and putting it altogether you can do:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <limits>
bool compare (const std::string& a, const std::string& b)
{
int suma = std::accumulate(a.begin(), a.end(), 0), /* sum values in a */
sumb = std::accumulate(b.begin(), b.end(), 0); /* sum values in b */
return suma < sumb; /* return comparison */
}
int main (void) {
int n = 0; /* int for 1st value input */
std::vector<std::string> vs {}; /* storage for each string */
if (!(std::cin >> n) || n < 0) { /* read n from input */
std::cerr << "invalid format - n.\n";
return 1;
} /* clear to end of line (removing \n) */
std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
while (n--) { /* loop n times */
std::string tmp; /* temporary string */
if (!getline (std::cin, tmp)) { /* read/validate input into tmp */
std::cerr << "error: failed read of string.\n";
return 1;
}
vs.push_back(tmp); /* add to vector */
}
std::sort (vs.begin(), vs.end(), compare); /* sort w/compare function */
for (auto& s : vs) /* loop over sorted strings */
std::cout << s << '\n'; /* output each string */
}
Example Input
$ cat dat/asciisum.txt
5
aaaaaaaaaa
ababababab
bbbbbbbbbb
acacacacad
ddaaaaaaaa
Example Use/Output
$ ./bin/sortasciivalsum < dat/asciisum.txt
aaaaaaaaaa
ababababab
ddaaaaaaaa
bbbbbbbbbb
acacacacad
Selection Sort Instead of std::sort
To use a selection-sort instead of the std::sort
algorithm, you would need to replace the compare()
function with:
void selsort (std::vector<std::string>& vs)
{
for (size_t i = 0; i < vs.size() - 1; i++) {
size_t minidx = i; /* set minimum index */
for (size_t j = i + 1; j < vs.size(); j++) { /* find new minidx */
/* compare sum of ASCII values between vs[j] & vs[minidx] */
if (std::accumulate(vs[j].begin(), vs[j].end(), 0) <
std::accumulate(vs[minidx].begin(), vs[minidx].end(), 0))
minidx = j; /* set min index to lesser */
}
if (minidx != i) /* if min index changed */
std::swap (vs[i], vs[minidx]); /* swap strings */
}
}
and then replace:
std::sort (vs.begin(), vs.end(), compare); /* sort w/compare function */
with
selsort (vs); /* selection sort based on ASCII sum */
The selection sort takes a reference to your collection of strings (your std::vector<std::string>
) and then compares the sum of the strings using std::accumulate
in the same way the compare()
function did in order to sort the strings according to the numeric sum of the characters contained in the string. The selection sort sequentially finds the string with the lowest sum and swaps those to the beginning in order.
As in the comment, you are always better off using std:sort
rather than a sort you write. std::sort
has been tested a billion times over and will be much more efficient. (however a selection sort isn't bad...)
Make the changes, look things over and let me know if you have further questions.