Alphabetizing a listbox (SOLVED)

Started by Rui 'Trovatore' Pires, Thu 05/01/2006 23:10:31

Previous topic - Next topic

Rui 'Trovatore' Pires

So, once again I'm having fun with a little project of mine. Ho hum.

I've almost come up with a way to add items to listboxes in an alphabetical order, which is:

Code: ags
function ValidateVerb(String item, ListBox *whereto, int index) {
  String item2=item.Copy();
  Parser.ParseText(item);
  String parseitem=Parser.SaidUnknownWord();
  if (parseitem!=null) {
    Display("Sorry, this game does not recognize that verb. Rest assured, it's not necessary to use it to complete the game.");
    return;
  }
  else {
    whereto.InsertItemAt(index, item2);
    return;
  }
}

function AdvancedSorting(String item, ListBox *whereto, int position, int counter, char old, char new) {
  if (counter

function AddItemAlphabetically(String item, ListBox *whereto) {
  int position=0;
  int counter=0;
  char new;
  char old;
  while (counter<whereto.ItemCount) {
    new=item.Chars[position];
    old=whereto.Items[counter].Chars[position];
    if (new>old) counter++;
    else if (new==old) {
      AdvancedSorting(item, whereto, position, counter, old, new);
      return;
    }
    else if (new<old) {
      ValidateVerb(item, whereto, counter);
      return;
    }
  }
  ValidateVerb(item, whereto, counter);  
  return;  
}


You'll notice AdvancedSorting is incomplete. Well, this is where I need help. As it is, it works better than I had hoped (i.e., apparently flawless), but if it finds two initial letters that happen to be the same... well, that's just it. I tried just adding "position++;" at the "else if (new==old) {" point, but obviously it just started checking on the second character, with no regard to the first one. This doesn't do.

I'm out of ideas. I mean, I have got a glimpse of some images - arrays? find in string? - but I can't put them together, and they're probably wrong anyway. This is getting a bit out of hand, and I don't want to end up making a "while" for each letter, as I usually do (shame on me).

Can anyone help?

EDIT - Before anyone questions some of those "return"s - a lot of this was copy/paste as glitches and oversights were fixed, and without me noticing some of it was even inside a "while" it shouldn't. So some "return"s were necessary before I fixed it, and then I played it safe and decided not to mess with them anymore.
Reach for the moon. Even if you miss, you'll land among the stars.

Kneel. Now.

Never throw chicken at a Leprechaun.

Kweepa

Try String.CompareTo(). I presume it's a wrapper for the C function strcmp, which returns 0 if the strings are the same, <0 if the first string should be first alphabetically, and >0 if the first string should be second alphabetically.

If that doesn't work, then the easiest thing to do is to write a function to compare the two strings letter by letter until they differ and use that instead of new>old, like this:

Code: ags

// returns the position that the strings differ + 1
// if a > b, returns a negative number
int StringSortPredicate(String a, String b)
{
   int length = a.Length;
   if (b.Length < a.Length) length = b.Length;

   int pos = 0;
   while (pos < length)
   {
      if (a.Chars[pos] < b.Chars[pos]) return -(pos+1);
      if (a.Chars[pos] > b.Chars[pos]) return pos+1;
      pos++;
   }
   // no characters differ in the first 'length' characters
   if (a.Length == b.Length) return 0; // the same
   if (a.Length > b.Length) return pos+1; // a longer than b, so later in dictionary
   return -(pos+1);
}
Still waiting for Purity of the Surf II

Rui 'Trovatore' Pires

#2
Thanks for your help - I'll try using your code when I can (read - in about 16 hours from now).

EDIT - Steve, you're a lifesaver! Thank you very much.

For anyone interested, here's the full code - works like a charm! Who knows, someone else may find it useful.

Code: ags
function ValidateVerb(String item, ListBox *whereto, int index) {
Ã,  String item2=item.Copy();
Ã,  Parser.ParseText(item);
Ã,  String parseitem=Parser.SaidUnknownWord();
Ã,  if (parseitem!=null) {
Ã,  Ã,  Display("Sorry, this game does not recognize that verb. Rest assured, it's not necessary to use it to complete the game.");
Ã,  Ã,  return;
Ã,  }
Ã,  else {
Ã,  Ã,  whereto.InsertItemAt(index, item2);
Ã,  Ã,  return;
Ã,  }
}

Ã,  Ã,  // returns the position that the strings differ + 1
Ã,  Ã,  // if a > b, returns a negative number
Ã,  Ã,  int StringSortPredicate(String a, String b) {
Ã,  Ã,  int length = a.Length;
Ã,  Ã,  if (b.Length < a.Length) length = b.Length;

Ã,  Ã, int pos = 0;
Ã,  Ã, while (pos < length)
Ã,  Ã, {
Ã,  Ã,  Ã,  if (a.Chars[pos] < b.Chars[pos]) return -(pos+1);
Ã,  Ã,  Ã,  if (a.Chars[pos] > b.Chars[pos]) return pos+1;
Ã,  Ã,  Ã,  pos++;
Ã,  Ã, }
Ã,  Ã, // no characters differ in the first 'length' characters
Ã,  Ã, if (a.Length == b.Length) return 0; // the same
Ã,  Ã, if (a.Length > b.Length) return pos+1; // a longer than b, so later in dictionary
Ã,  Ã, return -(pos+1);
}

function AddItemAlphabetically(String item, ListBox *whereto) {
Ã,  int position=0;
Ã,  int counter=0;
Ã,  char new;
Ã,  char old;
Ã,  while (counter<whereto.ItemCount) {
Ã,  Ã,  new=item.Chars[position];
Ã,  Ã,  old=whereto.Items[counter].Chars[position];
Ã,  Ã,  if (new>old) counter++;
Ã,  Ã,  else if (new==old) {
Ã,  Ã,  Ã,  Ã,  ValidateVerb(item, whereto, StringSortPredicate(item, whereto.Items[counter]));
Ã,  Ã,  Ã,  Ã,  return;
Ã,  Ã,  Ã, }
Ã,  Ã,  else if (new<old) {
Ã,  Ã,  Ã,  ValidateVerb(item, whereto, counter);
Ã,  Ã,  Ã,  return;
Ã,  Ã,  }
Ã,  }
Ã,  ValidateVerb(item, whereto, counter);Ã,  
Ã,  return;Ã,  
}


Note - it's case-sensitive. In my own uses I'm making sure that the string these functions work with is lowercase safe for the first letter, which is uppercase.

And to CJ if he happens to read this - thank you very much for making this sort of neat, flexible, tidy, powerful code possible with all the recent changes in AGS since it started using OO-style scripting.  :D
Reach for the moon. Even if you miss, you'll land among the stars.

Kneel. Now.

Never throw chicken at a Leprechaun.

Kweepa

I guess String.CompareTo didn't work then? :(
Still waiting for Purity of the Surf II

Rui 'Trovatore' Pires

#4
Very honestly, I didn't try it. I couldn't fit the logic around my head, much less write a function that would behave as I would like. Even your function I don't fully understand.

But I'm coming to, now that I'm having to modify it a bit. It didn't really work after all, you see - if I had two verbs in the listbox, "Examine" and "Look At", and tried to add Exhale or Exist it would quit with an error... it was returning index number 3. Whereas only values 0 or 1 would be acceptable. This didn't error out when it had more verbs in, but the placement was odd.

Looking through the code, I think I know why this happens... you use the variable "pos" to check the character's placement in the string, and I was using that same variable to check for the listboxes' index. If the word had many matching letters, naturally it didn't pan out.

But I'm on the right track now, using your code as a base. Thank you very much - I think I can handle it from here. If I can't I'll post here again.

EDIT - Ah, it's much better now. I'm not sure it's foolproof - I thought the old code was, too, until I tested more throughly - but if it ain't, it sure is fooling me.

Code: ags
function ValidateVerb(String item, ListBox *whereto, int index) {
Ã,  String item2=item.Copy();
Ã,  Parser.ParseText(item);
Ã,  String parseitem=Parser.SaidUnknownWord();
Ã,  if (parseitem!=null) {
Ã,  Ã,  Display("Sorry, this game does not recognize that verb. Rest assured, it's not necessary to use it to complete the game.");
Ã,  Ã,  return;
Ã,  }
Ã,  else {
Ã,  Ã,  whereto.InsertItemAt(index, item2);
Ã,  Ã,  return;
Ã,  }
}

// returns the position that the strings differ + 1
// if a > b, returns a negative number
int StringSortPredicate(String a, String b, int counter, ListBox *whereto) {
Ã,  int length = a.Length;
Ã,  if (b.Length < a.Length) length = b.Length;
Ã,  char new;
Ã,  char old;
Ã,  int pos = 0;
Ã,  while (pos < length) {
Ã,  Ã,  if (a.Chars[pos] < b.Chars[pos]) return counter;
Ã,  Ã,  if (a.Chars[pos] > b.Chars[pos]) {
Ã,  Ã,  Ã,  counter++;
Ã,  Ã,  Ã,  if (counter<whereto.ItemCount) {
Ã,  Ã,  Ã,  Ã,  new=a.Chars[0];
Ã,  Ã,  Ã,  Ã,  old=whereto.Items[counter].Chars[0];
Ã,  Ã,  Ã,  Ã,  if (new==old) {
Ã,  Ã,  Ã,  Ã,  Ã,  pos=0;
Ã,  Ã,  Ã,  Ã,  Ã,  b=whereto.Items[counter].Copy();
Ã,  Ã,  Ã,  Ã,  Ã,  length = a.Length;
Ã,  Ã,  Ã,  Ã,  Ã,  if (b.Length < a.Length) length = b.Length;
Ã,  Ã,  Ã,  Ã,  }
Ã,  Ã,  Ã,  Ã,  else return counter;
Ã,  Ã,  Ã,  }
Ã,  Ã,  Ã,  else return counter;
Ã,  Ã,  }
Ã,  Ã,  pos++;
Ã,  }
Ã,  // no characters differ in the first 'length' characters
Ã,  if (a.Length == b.Length) return 0; // the same
Ã,  if (a.Length > b.Length) return counter+1; // a longer than b, so later in dictionary
Ã,  return counter;
}

function AddItemAlphabetically(String item, ListBox *whereto) {
Ã,  int position=0;
Ã,  int counter=0;
Ã,  char new;
Ã,  char old;
Ã,  while (counter<whereto.ItemCount) {
Ã,  Ã,  new=item.Chars[position];
Ã,  Ã,  old=whereto.Items[counter].Chars[position];
Ã,  Ã,  if (new>old) counter++;
Ã,  Ã,  else if (new==old) {
Ã,  Ã,  Ã,  ValidateVerb(item, whereto, StringSortPredicate(item, whereto.Items[counter],counter, whereto));
Ã,  Ã,  Ã,  return;
Ã,  Ã,  }
Ã,  Ã,  else if (new<old) {
Ã,  Ã,  Ã,  ValidateVerb(item, whereto, counter);
Ã,  Ã,  Ã,  return;
Ã,  Ã,  }
Ã,  }
Ã,  ValidateVerb(item, whereto, counter);Ã,  
Ã,  return;Ã,  
}
Reach for the moon. Even if you miss, you'll land among the stars.

Kneel. Now.

Never throw chicken at a Leprechaun.

Pumaman

For information, the advantage of using CompareTo is that then you could just do something like this:


function AddItemAlphabetically(String item, ListBox *whereto) {
  int position=0;
  int counter=0;
  char new;
  char old;
  while (counter<whereto.ItemCount) {
    if (item.CompareTo(whereto.Items[counter]) > 0) counter++;
   else {
      ValidateVerb(item, whereto, counter);
      return;
    }
  }
  ValidateVerb(item, whereto, counter); 
  return; 
}

and then do away with StringSortPredicate completely.


SMF spam blocked by CleanTalk