Page 1 of 2
Sorting collections
Posted: Thu Jun 07, 2012 5:50 pm
by timmeh
If I have a collection of strings such as a StringArray, whats the most efficent way of sorting them alphabetically?
I'm used to having some inbuilt sort method although dont think thats an option in Jade 6.3?
Thanks,
Tim
Re: Sorting collections
Posted: Thu Jun 07, 2012 7:33 pm
by murray
You have to provide your own sort algorithm, selected to be best suited to your particular case.
For another approach, you could look at the possibilities of using transient JadeDynamicObjects with a DynaDictionary using external keys. This makes use of Jade's BTrees for ordering members. These can be useful for ad-hoc sorting. (Remember to delete all your transient objects afterwards!)
Re: Sorting collections
Posted: Fri Jun 08, 2012 1:50 am
by ghosttie
The only sorting method JADE provides is using dictionary keys. So you'd need to create a dictionary of dummy objects sorted the way you want, then for each string in your string array create a dummy object with the string as its key and add the dummy object to the dictionary. Then you'd need to loop over the dummy objects in the dictionary getting the current key value and adding that to a second string array which will end up being your sorted string array. I think you'll agree that this is less than ideal.
Alternatively you could implement one of the standard
sorting algorithms yourself. For example here is an implementation of the
Bubble Sort algorithm I translated from another language into JADE.
Constants:
- LessThan= -1
- EqualTo= 0
- GreaterThan= 1
StringArray::bubbleSort
Code: Select all
bubbleSort(pB_CaseSensitive : Boolean; pB_Ascending : Boolean) lockReceiver, updating;
vars
iSortedSoFar, iCurrentIndex, iAscDescComparator : Integer;
sCurrent, iNext : String;
begin
if size > 1 then
if pB_Ascending then
iAscDescComparator := GreaterThan;
else
iAscDescComparator := LessThan;
endif;
foreach iSortedSoFar in 1 to size - 1 do
foreach iCurrentIndex in 1 to size-iSortedSoFar do
sCurrent := self[iCurrentIndex];
iNext := self[iCurrentIndex + 1];
if sCurrent.compareTo(iNext, pB_CaseSensitive) = iAscDescComparator then
self[iCurrentIndex] := iNext;
self[iCurrentIndex + 1] := sCurrent;
endif;
endforeach;
endforeach;
endif;
end;
String::compareTo
Code: Select all
compareTo(pS_Comparer : String; pB_CaseSensitive : Boolean) : Integer;
vars
iIndex, iCurrent : Integer;
begin
if (pB_CaseSensitive and self = pS_Comparer) or (not pB_CaseSensitive and self.toLower = pS_Comparer.toLower) then
return EqualTo;
endif;
foreach iIndex in 1 to length.min(pS_Comparer.length) do
iCurrent := self[iIndex:1].Character.compareTo(pS_Comparer[iIndex:1].Character, pB_CaseSensitive);
if iCurrent <> EqualTo then
break;
endif;
endforeach;
if iCurrent = EqualTo then
if length > pS_Comparer.length then
return GreaterThan;
elseif length < pS_Comparer.length then
return LessThan;
else
return EqualTo;
endif;
else
return iCurrent;
endif;
end;
Character::compareTo
Code: Select all
compareTo(pC_Comparer : Character; pB_CaseSensitive : Boolean) : Integer;
vars
iSelf, iComparer : Integer;
begin
if pB_CaseSensitive then
iSelf := self.Integer;
iComparer := pC_Comparer.Integer;
else
iSelf := self.toLower.Integer;
iComparer := pC_Comparer.toLower.Integer;
endif;
if self.Integer > pC_Comparer.Integer then
return GreaterThan;
elseif self.Integer < pC_Comparer.Integer then
return LessThan;
else
return EqualTo;
endif;
end;
Re: Sorting collections
Posted: Fri Jun 08, 2012 9:59 am
by JohnP
Bubble sort is not hard to do. Depending on what you want, a hardcoded case can be quite simple.
In some situations you can use a sorted listbox. In one app, I needed some strings sorted during form processing, so I created an extra listbox (visible = false) and used that to sort the strings.
If the requirements are more robust, a DynaDictionary might be better, as Murray suggested.
Re: Sorting collections
Posted: Thu Jun 21, 2012 7:05 pm
by timmeh
I've started on this trying to use dynamicDictionaries with external keys
In the documentation an example creates two external keys, then uses the putAtKey method for each member
Code: Select all
dyna.addExternalKey(TimeStampInterval,8,false,false); // age
dyna.addExternalKey(TimeStampInterval,8,false,false);
foreach emp in root.allEmployeesByName do
dyna.putAtKey(emp.getAge, emp.getLengthOfService, emp);
endforeach;
put at key says it accepts two params
Code: Select all
putAtKey(keys: KeyType;value: MemberType) updating;
When I try passing additional keys I get
Code: Select all
Method called with an incorrect number of parameters- code: 1414
Is there something I'm missing, I havent seen any example of methods being overloaded before in Jade.
Anyone have experience with this?
cheers!
Re: Sorting collections
Posted: Thu Jun 21, 2012 7:49 pm
by murray
You're nearly there ... you just need to add:
after you have defined the two keys. This finalises the dictionary definition.
Re: Sorting collections
Posted: Thu Jun 21, 2012 10:04 pm
by murray
With external keys you can use the dictionary's BTree to sort just the key values without bothering about the member objects at all. In fact, the "members" can be whatever convenient object that you have to hand. It's only to provide a reference and it will never be used anyway:
Code: Select all
vars
dd : DynaDictionary;
obj : Object; // arbitrary object
iter : Iterator;
i : Integer;
str : String;
begin
create dd transient;
dd.setMembership ( Object ); // arbitrary class
dd.addExternalKey( String, 8, false, false ); // define collection
dd.addExternalKey( Integer, 8, false, false );
dd.endKeys( false );
dd.putAtKey( "cat", 2, app ); // load up some random data
dd.putAtKey( "dog", 2, app );
dd.putAtKey( "cat", 1, app );
dd.putAtKey( "cat", 3, app );
dd.putAtKey( "ant", 2, app );
iter := dd.createIterator; // list key values in order
while iter.next( obj ) do
iter.getCurrentKeys( str, i );
write str & Tab & i.String;
endwhile;
epilog
delete dd;
delete iter;
end;
gives the output:
ant 2
cat 1
cat 2
cat 3
dog 2
Re: Sorting collections
Posted: Fri Jun 22, 2012 11:20 am
by timmeh
Okay cool!
Almost there, I had the endkeys fine, my issue was that I dont know how many keys the user will have.
I'n essence what I am doing is creating a html table that has sorting functionality. and using dynaDictionaries to created the sorted collection.
something like this:
http://datatables.net/examples/basic_in ... _sort.html
My issue with the too many params was because I tried passing null on external keys that are not being used, bellows the code to try and explain better.
There is 7 columns, each with a sort option - "sortOptions" { 0 = dont sort, 1 = ascending, 2 = decending }
Code: Select all
create dynaDict transient; //DynaDitionary
create keysArray transient; //StringArray
dynaDict.setMembership(M2SmRequestItem);
if sortOptions.key1 <> 0 then
if sortOptions.key1 = 1 then dynaDict.addExternalKey(Integer,10,false,false); else
dynaDict.addExternalKey(Integer,10,true,false); endif;
endif;
if sortOptions.key2 <> 0 then
if sortOptions.key2 = 1 then dynaDict.addExternalKey(TimeStamp,8,false,false); else
dynaDict.addExternalKey(TimeStamp,8,true,false); endif;
endif;
if sortOptions.key3 <> 0 then
if sortOptions.key3 = 1 then dynaDict.addExternalKey(String,150,false,false); else
dynaDict.addExternalKey(String,150,true,false); endif;
endif;
if sortOptions.key4 <> 0 then
if sortOptions.key4 = 1 then dynaDict.addExternalKey(Integer,4,false,false); else
dynaDict.addExternalKey(Integer,4,true,false); endif;
endif;
if sortOptions.key5 <> 0 then
if sortOptions.key5 = 1 then dynaDict.addExternalKey(String,150,false,false); else
dynaDict.addExternalKey(String,150,true,false); endif;
endif;
if sortOptions.key6 <> 0 then
if sortOptions.key6 = 1 then dynaDict.addExternalKey(String,150,false,false); else
dynaDict.addExternalKey(String,150,true,false); endif;
endif;
if sortOptions.key7 <> 0 then
if sortOptions.key7 = 1 then dynaDict.addExternalKey(Integer,4,false,false); else
dynaDict.addExternalKey(Integer,4,true,false); endif;
endif;
dynaDict.endKeys(true);
foreach req in pList.M2SmRequestItemDictionary do
keysArray.purge;
keysArray.add(null);keysArray.add(null);keysArray.add(null);
keysArray.add(null);keysArray.add(null);keysArray.add(null);
keysArray.add(null);keysArray.add(null);keysArray.add(null);
if sortOptions.key1 <> 0 then
keysArray.insert(1,req.uid1.String);
endif;
if sortOptions.key2 <> 0 then
keysArray.insert(2,req.created.String);
endif;
if sortOptions.key3 <> 0 then
if req.EbdAccessCardRI.myRequestItemType <> null then
keysArray.insert(3,req.EbdAccessCardRI.myRequestItemType.name);
endif;
endif;
if sortOptions.key4 <> 0 then
if req.EbdAccessCardRI.cardAmount <> null then
keysArray.insert(4,req.EbdAccessCardRI.cardAmount.String);
endif;
endif;
if sortOptions.key5 <> 0 then
if req.EbdAccessCardRI.myOrganisation <> null then
keysArray.insert(5,req.EbdAccessCardRI.myOrganisation.name);
endif;
endif;
if sortOptions.key6 <> 0 then
if req.EbdAccessCardRI.myOrganisation <> null then
keysArray.insert(6,req.EbdAccessCardRI.myOrganisation.name);
endif;
endif;
if sortOptions.key7 <> 0 then
if req.myPriority <> null then
keysArray.insert(7,helper_reqFlagInt(req).String);
else
keysArray.insert(7,'6');
endif;
endif;
if sortOptions.key8 <> 0 then
keysArray.insert(8,null);
endif;
dynaDict.putAtKey(keysArray[1].Integer,keysArray[2].TimeStamp,keysArray[3],keysArray[4].Integer,keysArray[5],keysArray[6],keysArray[7].Integer,req);
endforeach;
iter := dynaDict.createIterator;
So I understand that I'm passing too many keys, as if I pass null it still expects an externalKey to be there which isnt the case if the user picks only 3 of the 7 sort options.
What I want to find out still is there a way I can pass say a params array or somehow dynamically enter the put at key? or is the only way I can go about it say
Code: Select all
if ExternalKey.size = 5 then
dynaDict.putAtKey(keysArray[1]....
else if ExternalKey.size = 4 then
dynaDict.putAtKey(keysArray[1]....
else if ExternalKey.size = 3 then
dynaDict.putAtKey(keysArray[1]....
else if ExternalKey.size = 2 then
dynaDict.putAtKey(keysArray[1]....
etc
Re: Sorting collections
Posted: Fri Jun 22, 2012 12:05 pm
by BeeJay
I'd go with 7 String keys on the ExtKeyDynaDict, and then if they've only chosen 3 of 7 sort options, set the remaining key values to a null string as you don't care about the sort ordering from that point onwards.
Then, when you're deciding what to use for each of the 7 string key parameters to the putAtKey, you can "generate" a string key from the underlying data in a format that will sort correctly regardless of the type of data that is actually in that column.
Cheers,
BeeJay.
Re: Sorting collections
Posted: Fri Jun 22, 2012 12:18 pm
by timmeh
I'd go with 7 String keys on the ExtKeyDynaDict, and then if they've only chosen 3 of 7 sort options, set the remaining key values to a null string as you don't care about the sort ordering from that point onwards.
Then, when you're deciding what to use for each of the 7 string key parameters to the putAtKey, you can "generate" a string key from the underlying data in a format that will sort correctly regardless of the type of data that is actually in that column.
Cheers,
BeeJay.
I tried this just now which is almost perfect although it breaks on timestamps, as it sorts them as a string value and 01/01/2012 is lower then 10/01/1999