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:

Code: Select all

dyna.endKeys( allowDuplicates );
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