Skip to main content
Announcements
Qlik Connect 2024! Seize endless possibilities! LEARN MORE
cancel
Showing results for 
Search instead for 
Did you mean: 
rharmsny
Contributor
Contributor

creating a string of sorted variable values

In an action, I'd like to create a list of values from 5 variables into a sorted list.

Example:

var1 = -10

var2 = 0

var3 = -20

var4 = 20

var5= 10

I'd like to load a variable with a list that I can use the "pick" function against in sorted order so the above example would look like this:

varsort = -20, -10, 0, 10, 20

If the values are the same, it doesn't matter which variable gets loaded first

Any ideas?

10 Replies
Not applicable

Hi

what about a quick sort with using $-expressions to refer to the variable names?

loop over all var's until no exchange of values occur anymore.

Regards

Juerg

rharmsny
Contributor
Contributor
Author

thanks Juerg, but does that imply use a macro? I was hoping to get it with expressions only

johnw
Champion III
Champion III


RHARMSNY wrote:
thanks Juerg, but does that imply use a macro? I was hoping to get it with expressions only<div></div>


Expressions only? Sure! To propose an absolutely horrible solution, you can use a nested if() statement to do every possible sequence of necessary comparisons. Not certain mine is optimal, but I wrote an algorithm and code generator to produce the minimum number of comparisons and assignment operations for four through seven keys as part of a high-performance quicksort. The nested if for five keys is "only" 990 lines of code in my resulting program. And since you only need a single assignment each, your expression might be only half that size! Easy! Wink

I wish I could now give you a GOOD solution, but I can't think of one. I was thinking one of the range...() functions might do something useful, but while you can get the min and max, it doesn't look like you can get the in-between values. And even if you could, it doesn't sound very elegant. Maybe you could use... oh, what are the enterable fields called? Make one field with five values that you can enter, then use concat() to assemble your final string?

Edit: A bit off topic and almost purely for my own amusement, the generated five key sort starts out like this. And yes, I'm a COBOL dinosaur.

IF Q-KEY (Q-L ) <= Q-KEY (Q-L + 1)
IF Q-KEY (Q-L + 2) <= Q-KEY (Q-L + 3)
IF Q-KEY (Q-L ) <= Q-KEY (Q-L + 2)
IF Q-KEY (Q-L + 2) <= Q-KEY (Q-L + 4)
IF Q-KEY (Q-L + 3) <= Q-KEY (Q-L + 4)
IF Q-KEY (Q-L + 1) <= Q-KEY (Q-L + 3)
IF Q-KEY (Q-L + 1) > Q-KEY (Q-L + 2)
MOVE Q-RECORD (Q-L + 1) TO Q-SWAP-RECORD
MOVE Q-RECORD (Q-L + 2) TO Q-RECORD (Q-L + 1)
MOVE Q-SWAP-RECORD TO Q-RECORD (Q-L + 2)
END-IF
ELSE
IF Q-KEY (Q-L + 1) <= Q-KEY (Q-L + 4)
MOVE Q-RECORD (Q-L + 1) TO Q-SWAP-RECORD
MOVE Q-RECORD (Q-L + 2) TO Q-RECORD (Q-L + 1)
MOVE Q-RECORD (Q-L + 3) TO Q-RECORD (Q-L + 2)
MOVE Q-SWAP-RECORD TO Q-RECORD (Q-L + 3)
ELSE
MOVE Q-RECORD (Q-L + 1) TO Q-SWAP-RECORD
MOVE Q-RECORD (Q-L + 2) TO Q-RECORD (Q-L + 1)
MOVE Q-RECORD (Q-L + 3) TO Q-RECORD (Q-L + 2)
MOVE Q-RECORD (Q-L + 4) TO Q-RECORD (Q-L + 3)
MOVE Q-SWAP-RECORD TO Q-RECORD (Q-L + 4)
END-IF
END-IF
ELSE
IF Q-KEY (Q-L + 1) <= Q-KEY (Q-L + 4)
IF Q-KEY (Q-L + 1) <= Q-KEY (Q-L + 2)

Not applicable

Hi John

Where do you take the time from to create such answers???

I almost feel obliged now to publish a quicksort just to show that it will not take 990 lines of code Tongue Tied, and it will also be able to do 100 vars... (did you try out your generator for the 100 var case?)

Wait - maybe RHARMSNY has already started to write the macro? If so - please publish - we are looking forward to get a proof of your skills ...

Juerg

Not applicable

Ahh John! Bubble-sorting was a technique I used many years ago when writing COBOL (the '68 compiler didnt have a SORT!). It was so-called because the highest values floated to the top. Lots of WS-SUBn and PERFORM...VARYING

Basically an array of values was compared, each element with its neighbour - if it was larger then their positions were swapped and the flag set on to indicate a change had occured in the array. This was repeated until the array was checked again and the flag was still off to indicate no swap had been performed i.e. the array was in sequence.

Nice to think this could still have value!

Regards,

Gordon

Not applicable

Hi Gordon

Yes, actually I had bubble sort in mind, my solution



sub bubblesort()
Var1 = 5
Var2 = 3
Var3 = 4
numVar = 3
Exchange = 1
While Exchange = 1
Exchange = 0
For i = 1 to numVar-1
if eval("Var"&i+1) < eval("Var"&i) then
Var = eval("Var"&i+1)
Execute("Var"&i+1&"=Var"&i)
Execute("Var"&i&"=Var")
Exchange = 1
end if
next
Wend
For i = 1 to numVar
msgbox("Val"&i&"="&eval("Var"&i))
next
end sub


rharmsny
Contributor
Contributor
Author

Thank you for confirming my suspicion that lots of swapping would be required. I'll petition for a rangesort() function!

P.S - that's a nice generator you have there.

rharmsny
Contributor
Contributor
Author

Thanks so much! This should do it. I have a number of triggers that are setting the individual variables - i'll close out each one with the macro to resort the existing values at the end.

johnw
Champion III
Champion III


Juerg Maier JmiD GmbH Schweiz wrote:did you try out your generator for the 100 var case?


I'm not even sure I understand the question. The sort is generic in the sense that it simply compares keys. Your key could be 100 variables long, or 1000 variables long, and it wouldn't care. The generator itself is just trying to find a very good if/then/else structure and the best moves for sorting a particular sub-array, which just involves generating the key comparisons rather than worrying about how the key itself is structured.


Juerg Maier JmiD GmbH Schweiz wrote:I almost feel obliged now to publish a quicksort just to show that it will not take 990 lines of code Tongue Tied,


Actually, my FULL algorithm is 65181 lines of code. It includes optimal (?) sorts for sub arrays of up to 7 records, which requires generating absolutely huge if/then/else structures. I was doing everything I could to get every last microsecond of performance, because this particular sort algorithm probably executes hundreds of millions of times every day. (And for the sort experts, yes, I tested some o(n) sorts, but with our longish keys and shortish arrays, they were slower than the o(n log n) choices in practice. Much slower, if I recall.)

But yes, a basic quicksort is a very simple and still very efficient algorithm. It's a little more complicated in COBOL since COBOL doesn't support recursion, so you have to manage your own stack. It's probably several times longer than it would be in a more sophisticated language. It's still not a particularly difficult problem, though. Here's my baseline, simple quicksort from a suite of sorts that I wrote and tested while trying to optimize the real sort I was working on:

SIMPLE-QUICKSORT.

**** QUICKSORT IS THE STANDARD FAST SORTING ALGORITHM.
**** THIS IS A SIMPLE BUT INEFFICIENT IMPLEMENTATION.
**** FAST O(N LOG N) IN AVERAGE CASE, BUT O(N**2) WORST CASE,
**** WHICH HAPPENS WHEN RESORTING A SORTED ARRAY.
**** ALMOST 100 TIMES FASTER THAN BUBBLE SORT ON AN ARRAY OF 1000.

SET Q-STACK-DEX TO 1
MOVE 1 TO Q-STACK-L (1)
MOVE Q-RECORDS TO Q-STACK-R (1)

PERFORM UNTIL Q-STACK-DEX < 1
SET Q-L Q-I TO Q-STACK-L (Q-STACK-DEX)
SET Q-R Q-J TO Q-STACK-R (Q-STACK-DEX)
SET Q-STACK-DEX DOWN BY 1
IF Q-L < Q-R
MOVE Q-KEY (Q-L) TO Q-PIVOT-KEY
PERFORM UNTIL Q-I > Q-J
PERFORM UNTIL Q-KEY (Q-I) >= Q-PIVOT-KEY
SET Q-I UP BY 1
END-PERFORM
PERFORM UNTIL Q-KEY (Q-J) <= Q-PIVOT-KEY
SET Q-J DOWN BY 1
END-PERFORM
IF Q-I <= Q-J
MOVE Q-RECORD (Q-I) TO Q-SWAP-RECORD
MOVE Q-RECORD (Q-J) TO Q-RECORD (Q-I)
MOVE Q-SWAP-RECORD TO Q-RECORD (Q-J)
SET Q-I UP BY 1
SET Q-J DOWN BY 1
END-IF
END-PERFORM
SET Q-STACK-DEX UP BY 1
SET Q-STACK-L (Q-STACK-DEX) TO Q-I
SET Q-STACK-R (Q-STACK-DEX) TO Q-R
SET Q-STACK-DEX UP BY 1
SET Q-STACK-L (Q-STACK-DEX) TO Q-L
SET Q-STACK-R (Q-STACK-DEX) TO Q-J
END-IF
END-PERFORM
.
SIMPLE-QUICKSORT-EXIT. EXIT.

Here's my bubble sort for comparison. I compare all of the sorts in the suite to bubble sort. Bubble sort is the baseline. While I included it in my suite for comparison purposes, I wanted to make sure that nobody used it, thus the comment.

BUBBLE-SORT.

**** THE GENERIC BAD SORT ALGORITHM.
**** NEVER USE THIS SORT. BAD PROGRAMMER. DROP IT. GOOD BOY.

PERFORM WITH TEST AFTER UNTIL NO-SWAPS
SET NO-SWAPS TO TRUE
PERFORM VARYING Q-I FROM 1 BY 1 UNTIL Q-I = Q-R
IF Q-KEY (Q-I) > Q-KEY (Q-I + 1)
MOVE Q-RECORD (Q-I ) TO Q-SWAP-RECORD
MOVE Q-RECORD (Q-I + 1) TO Q-RECORD (Q-I )
MOVE Q-SWAP-RECORD TO Q-RECORD (Q-I + 1)
SET SWAPS TO TRUE
END-IF
END-PERFORM
END-PERFORM
.
BUBBLE-SORT-EXIT. EXIT.

Here's insertion sort, which is a very good combination of simple and "fast enough" on small arrays. This is what I would use if I just needed a quick and dirty sort for a small array.

INSERTION-SORT.

**** A SHORT AND EFFICIENT-FOR-THE-SIZE O(N**2) SORT ALGORITHM.
**** SEVEN TIMES FASTER THAN BUBBLE SORT ON AN ARRAY OF 1000.
**** THIS IMPLEMENTATION REQUIRES ONE EMPTY ARRAY RECORD.
**** THERE ARE FASTER IMPLEMENTATIONS, BUT THIS SEEMED SIMPLEST.

PERFORM VARYING Q-I FROM 2 BY 1 UNTIL Q-I > Q-R
MOVE Q-RECORD (Q-I) TO Q-RECORD (Q-R + 1)
PERFORM VARYING Q-J FROM Q-I BY -1 UNTIL Q-J = 1
OR Q-KEY (Q-J - 1) <= Q-KEY (Q-R + 1)
MOVE Q-RECORD (Q-J - 1) TO Q-RECORD (Q-J)
END-PERFORM
MOVE Q-RECORD (Q-R + 1) TO Q-RECORD (Q-J)
END-PERFORM
.
INSERTION-SORT-EXIT. EXIT.