I made a qsort function for a larger program. It is to sort by time. I have a class schedule I am working with and found a need to compare time with AM and PM. ie if A is chosen then all the classes in the morning, if P is chosen all the classes in the afternoon. My question is this, is there a way to use this sort function with a greater than or less than comparator? If so can someone show me how if it is not to much trouble?
int sortFunction(const void *p, const void *q) {
return ((sched_record *) p)->start.hour -
((sched_record *) q)->start.hour;
}
Writing a comparator
In C, the comparator function from
qsort()returns a number less than, equal to, or greater than zero according to whether the data structure represented by the first argument should be sorted before, equal to, or after the second parameter.Sorting am and pm times is painful; even converting from am/pm to 24-hour is not entirely trivial. It is far better to store time values in 24 hour notations (or even as seconds since The Epoch). The presentation layer should deal with presenting the time in am/pm notation; the model and controller layers should usually avoid messing with am/pm. Don’t forget:
Assuming you are not constrained to events starting on the hour and that you have resolved to use 24 hour times internally, then you can write code such as:
It is fairly obvious how to extend that to manage seconds or other match criteria. Note that this code does not run the risk of integer overflow, whereas subtracting two numbers could in general run into overflow problems.
If you decide to continue with 12 hour clocks, then you will have to have a way to distinguish 6 am from 6 pm. Basically, you’ll convert your 12 hour notation into 24 hour notation in the function, then do the comparison on that basis (I’m assuming AM and PM are enumeration constants):
How might this be used?
Usage is fairly simple. Somewhere in your program, you have an array of
sched_records:That’s all there is to it. It could be a fixed size array instead:
Then, assuming you still have a variable
size_t num_recordswhich indicates how many of the records are in use, you use the sameqsort()call. Usingqsort()is very straight-forward. Usingbsearch()is slightly more complex, because you typically have to fake up a record to find:Using C99 and designated initializers makes it easier. You have to ensure that your key record has an appropriate value in each of the fields that will be used by the comparator. Of course, you’ve already sorted the array with
qsort()before you usebsearch()on it — or made sure that the data is in the same sorted order ‘as if’ you had done aqsort()on it with the same comparator.It’s also worth writing a function to check the sort order of an array — it is straight-forward, and left as an ‘exercise for the reader’. You can then use that in assertions, for example.
Don’t write your own
qsort()I note that all of us answering the question are assuming that you are using the Standard C Library sort function, yet your question suggests you’ve written your own. Generally speaking, you’ll have to be very good to do better than the system-provided
qsort(); I wouldn’t bother to write my own unless I could demonstrate that the system function was too slow.qsort()until you don’t need to ask how to write one for yourself.If you must still write the code, then you need to decide on the interface. You can either mimic the standard interface (but use a different name), or you can write a custom interface tied to one specific type (which must be re-parameterized if you need to sort a different type). The latter is roughly what C++ does with templates.
One of the problems for writing your own generic comparator is swapping the elements when you don’t know how big the elements will be in advance. If you can use C99 and VLAs, it isn’t too bad, though if the size of an element blows your stack, then you’re completely hosed.
Inside the function, you have to be careful to use
char *instead ofvoid *because you can’t legitimately do pointer arithmetic onvoid *, notwithstanding that GCC does allow you to do so as a non-standard extension.You need to make sure you have a clear picture of how the data is laid out, and what your sorting code will be doing to it. You’ll take a comparator like the ones described in the various answers, and when you need to do a comparison, you’ll do something like:
You can then do:
Showing Different Ranges
You’re confusing two things: sorting the data and presenting the right subset of the data. You sort the data as discussed/shown. This gives you a sorted array. Then you scan through the array to present the entries in the time range you are interested in. That will be a separate function; you might well still use the comparator function, but you’d create a pair of dummy keys for the start and end of the time range (each key would be a bit like the key in the
bsearch()example in my answer) and then look for all the records in the sorted array after the start time and before the end time.I’m about to make some simplifying assumptions. Your
start.hourrecords the time unambiguously as a number of minutes since midnight, and it is an integer.Sort the array:
Generate the correct keys —
loandhi:Write the
SchedRangeSearch()function:Use the search function to find the range required:
Show the relevant records:
Untested code: beware crashes, out of bounds access, etc.
The intention is that the search function provides two pointers, to the start (first valid item in the range) and end of the range, where the end pointer points beyond the last valid item. Hence the
forloop to display the valid data goes from start to strictly less than end. (This is the same convention used in C++ with STL iterators. It pays to reuse good ideas.)