Game Development Community

dev|Pro Game Development Curriculum

new console functions: SortWords() and SortNumbers()

by Orion Elenzil · 11/25/2006 (10:00 pm) · 7 comments

i was a little surprised this wasn't already implemented. maybe they are in TGE 1.4 or 1.5.
this routines are funny, 100% of the work involved is really just converting from space-delimited args to arrays and back again. anyhow.

[b]usage:[/b]
SortWords  (<space     -delimited list of words  >, [ignoreCase = true], [increasing = true])
SortFields (<tab       -delimited list of words  >, [ignoreCase = true], [increasing = true])
SortRecords(<newline   -delimited list of words  >, [ignoreCase = true], [increasing = true])
SortNumbers(<whitespace-delimited list of numbers>, [increasing = true])

note the difference between actual space (space) and whitespace (space, tab, cr or nl)

To implement, simply copy the following hunk of code to the bottom (or wherever) of consoleFunctions.cc
ConsoleFunctionGroupBegin( Sorting, "Functions for sorting words, numbers, etc.");

static bool sIgnoreCase;
static bool sIncreasing;

static S32 QSORT_CALLBACK qSortCallbackTextCompare(const void* a, const void* b)
{
   StringTableEntry* ea = (StringTableEntry*)(a);
   StringTableEntry* eb = (StringTableEntry*)(b);
   S32 result;
   
   if (sIgnoreCase)
      result = dStricmp(*ea, *eb);
   else
      result = dStrcmp (*ea, *eb);

   if (!sIncreasing)
      result = -result;

   return result;
}

static S32 QSORT_CALLBACK qSortCallbackFloatCompare(const void* a, const void* b)
{
   float* ea = (float*)(a);
   float* eb = (float*)(b);
   
   S32 result;
   
   // note we are comparing floats, and will *NEVER* return "equal".
   result = (*ea < *eb) ? -1 : 1;

   if (!sIncreasing)
      result = -result;

   return result;
}

const char* SortUnits(const char* units, bool ignoreCase, bool increasing, const char* delimiter)
{
   sIgnoreCase = ignoreCase;
   sIncreasing = increasing;

   // extract the words
   S32 numItems = getUnitCount(units, delimiter);

   if (numItems < 2)
      return units;

   char** items  = new char*[numItems];

   for (S32 n = 0; n < numItems; n++)
   {
      const char* item = getUnit(units, n, delimiter);
      items[n]         = dStrdup(item);
   }

   // actually do some work
   dQsort((void *)items, numItems, sizeof(*items), qSortCallbackTextCompare);

   // put em into a return buffer
   char *ret = Con::getReturnBuffer(dStrlen(units) + 1);
   ret[0]    = '[[6281528f2d2e3]]';

   for (S32 n = 0; n < numItems; n++)
   {
      if (n > 0)
         dStrcat(ret, delimiter);
      dStrcat(ret, items[n]);
   }

   // free the array
   for (S32 n = 0; n < numItems; n++)
      dFree(items[n]);

   delete [] items;

   return ret;
}

ConsoleFunction(SortWords, const char *, 2, 4,"SortWords(words, [ignoreCase=true], [increasing=true])")
{
   bool ignoreCase = true;
   bool increasing = true;

   if (argc > 2)
      ignoreCase = dAtob(argv[2]);

   if (argc > 3)
      increasing = dAtob(argv[3]);

   return SortUnits(argv[1], ignoreCase, increasing, " ");
}

ConsoleFunction(SortFields, const char *, 2, 4,"SortFields(words, [ignoreCase=true], [increasing=true])")
{
   bool ignoreCase = true;
   bool increasing = true;

   if (argc > 2)
      ignoreCase = dAtob(argv[2]);

   if (argc > 3)
      increasing = dAtob(argv[3]);

   return SortUnits(argv[1], ignoreCase, increasing, "\t");
}

ConsoleFunction(SortRecords, const char *, 2, 4,"SortRecords(words, [ignoreCase=true], [increasing=true])")
{
   bool ignoreCase = true;
   bool increasing = true;

   if (argc > 2)
      ignoreCase = dAtob(argv[2]);

   if (argc > 3)
      increasing = dAtob(argv[3]);

   return SortUnits(argv[1], ignoreCase, increasing, "\n\r");
}

ConsoleFunction(SortNumbers, const char *, 2, 4,"SortNumbers(numbers, [increasing=true])")
{
   // extract the numbers as floats.
   S32 numItems = getUnitCount(argv[1], " \t\n\r");

   if (numItems < 2)
      return argv[1];

   F32* items  = new F32[numItems];

   for (S32 n = 0; n < numItems; n++)
   {
      const char* item = getUnit(argv[1], n, " \t\n\r");
      items[n]         = dAtof(item);
   }


   // get our optional arguments
   if (argc > 2)
      sIncreasing = dAtob(argv[2]);
   else
      sIncreasing = true;


   // actually do some work
   dQsort((void*)items, numItems, sizeof(*items), qSortCallbackFloatCompare);


   // put em into a return buffer
   char  buf[16];  // should be enough room, neh ? 13 is the largest i see: "1.23456e+789 "
   char *ret = Con::getReturnBuffer(numItems * sizeof(buf) + 1);
   ret[0]    = '[[6281528f2d2e3]]';

   for (S32 n = 0; n < numItems; n++)
   {
      if (n > 0)
         dSprintf(buf, sizeof(buf), " %g", items[n]);
      else
         dSprintf(buf, sizeof(buf), "%g" , items[n]);

      dStrcat(ret, buf);
   }


   // free the array
   delete [] items;

   return ret;
}
ConsoleFunctionGroupEnd( Sorting );

examples:
==>echo(SortWords("dog cat Orion mouse 100 37"));
100 37 cat dog mouse Orion

==>echo(SortWords("dog cat Orion mouse 100 37", false));
100 37 Orion cat dog mouse

==>echo(SortWords("dog cat Orion mouse 100 37", true, false));
Orion mouse dog cat 37 100

==>echo(SortNumbers("100 37 2.3 -2.3 -3.2"));
-3.2 -2.3 2.3 37 100

==>echo(SortNumbers("100 37 2.3 -2.3 -3.2", false));
100 37 2.3 -2.3 -3.2

#1
11/27/2006 (5:33 am)
Could be useful for string lists, thanks!
#2
11/27/2006 (6:25 am)
Very handy indeed :)
#3
11/30/2006 (12:50 am)
realized this would be more useful if it weren't hardwired for space-delimited entries.
will soon modify to accept a list of delimiters, with space, tab, and newline as defaults.
#4
11/30/2006 (7:43 am)
Drop 'n' Play

Simple, quick & effective - Thank You :D
#5
03/02/2007 (5:01 pm)
expanded this to include SortFields() and SortRecords().
#6
03/02/2007 (5:34 pm)
see also related resource findWord() etc.
#7
08/06/2008 (4:34 pm)
I had some problems with TGB 1.7 with the following errors:

error C3861: 'getUnitCount': identifier not found
error C3861: 'getUnit': identifier not found
error C3861: 'getUnitCount': identifier not found

The solution is to add a "StringUnit::" prior to each function call - so "StringUnit::getUnit(" etc.

Thanks to Alex Scarborough for this fix :)