Jump to content
Sign in to follow this  
Mike Torrettinni

How to deal with different types of Variant?

Recommended Posts

For Sorting in VirtualTreeview I use CompareNodes method, which compares column values and sorts lines.

 

In CompareNodes  I use CompareValues method to compare Integer or String values - 2 overloaded methods:

function CompareValues(const aValue1, aValue2: string): integer; overload;
begin
    Result := AnsiCompareText(aValue1, aValue2);
end;

function CompareValues(const aValue1, aValue2: integer): integer; overload;
begin
  Result := CompareValue(aValue1, aValue2);
end;

This works as expected.

 

But now I need to compare Variant values which can hold Integer or String, so I setup this method:

function CompareValues(const aValue1, aValue2: Variant): integer; overload;
var vVarType1, vVarType2: integer;
Begin
    Result := 0;
    vVarType1 := VarType(aValue1); 
    vVarType2 := VarType(aValue2);
    
    // if ANY of the values is string, should compare as strings!
    if (vVarType1 = varString) or (vVarType1 = varUString) or (vVarType2 = varString) or (vVarType2 = varUString) then
      Result := CompareValues(String(VarAsType(aValue1, varString)), String(VarAsType(aValue2, varString))) // <- without String cast if thinks the values are Variant and calls itself!
 
    else if VarType(aValue1) = varInteger then
      Result := CompareValues(Integer(VarAsType(aValue1, varInteger)), Integer(VarAsType(aValue2, varInteger))); // <- have to cast as Integer!
End;

I set it up like this because when trying to sort values in column that have integers and strings, like:

 

var vVariant1, vVariant2: Variant;
begin
   vVariant1 := 5; vVariant2 := 'Yes';
   if CompareValues(vVariant1, vVariant2) = 0 then
     Showmessage('Variant values are equal.') else Showmessage('Variant values are NOT equal.')

it works.

 

But, why does it:

  a. String 'Yes' recognizes as varUString and not varString, so I need to check Variant for both, just in case

  b. If I don't cast values when calling CompareValues to Integer or String, it calls as Variant types again and I get Stack overflow error, of course.

 

So, I have the following questions:

1) Can I force VarType to return varString for varString or varUString? So I will only have 1 comparison, like VarType(aValue1) = varString, and not need to compare with 2 different string types...

2) There are far more number types of Variant (varByte, varWord, varShortInt, varInteger...)... how can I be sure that all numbers will always be recognized as varInteger?

3) Why do I need to cast VarAsType? String(VarAsType(aValue1, varString)) and Integer(VarAsType(aValue1, varInteger)), even though I specify I want varString or varInteger...

 

Any advice is welcome! Thanks!

 

 

 

Share this post


Link to post

You are working with Unicode strings so you will always get varUString. I don't know what you have against varUString. Surely you don't want to go back to ANSI strings which is what varString represents. 

 

VarAsType returns a variant. That's why you need a typecast to obtain other types. 

 

The entire question seems massively over complicated. In fact this is a common theme of the questions you ask. Don't take that the wrong way. But my most important advice to you is to try to simplify. You seem regularly to get tied in knots because you don't reduce the complexity in your thinking. 

 

In this instance, surely, you should be working with the user input as text. No need at all for variants in my opinion. Simply use TryStrToInt to check if a string value can be treated as an integer. 

 

And if there really is some need for variants due to motivations we cannot see then take care to understand what types they can hold. Make sure that you only ever add Unicode strings and integers and therefore only need to deal with those two var types. Raise assertion exceptions if you encounter other types. Make sure that you have good testing in place. 

 

Now, you probably think that you haven't got time for testing but what many people don't realise is that testing saves you time. Testing is what allows you to make changes to code without fear of breaking it. 

 

 

  • Like 2

Share this post


Link to post

There are several functions to simplify your task. You can use VarIsStr to check for string variants and VasIsOrdinal for the Integer case. Also there is VarToStr to convert a Variant to string.

 

With this you can strip down your compare function to 

function CompareValues(const aValue1, aValue2: Variant): integer; overload;
Begin
  Result := 0;
  // if ANY of the values is string, should compare as strings!
  if VarIsStr(aValue1) or VarIsStr(aValue2) then
    Result := CompareValues(VarToStr(aValue1), VarToStr(aValue2))
  else if VarIsOrdinal(aValue1) and VarIsOrdinal(aValue2) then // Make sure that the following cast succeeds!
    Result := CompareValues(Integer(aValue1), Integer(aValue2));
End;

 

  • Like 1

Share this post


Link to post

@Uwe Raabe Works good on my data. Very rarely I use Variants, so I didn't see these methods. Thanks!

 

@David Heffernan Thank you, I see now that VarAsType returns a new Variant and not as the type you specify, odd. (I should read documentation more precise)

 

As for the why I need this: I have a VirtualTreeview report that shows 20+ different categories of Customer data and 3-30 different columns, depending on the selected category.

So, I need at least OnGetText, OnCompareNodes to show and sort data, then I have my own Copy, Search.. and Export data methods. So instead of assigning data to column in each method (like CellText := Name; or CellText := IntToStr(UnpaidInvoices)), I'm testing with only ONCE specifying what data is in each column of the view, so I have:

function ReturnColumnData(Category, ColumnNo): Variant;

 

and I can use it any of the methods that needs data for each column,

And Export method can create Excel file and I need to export numbers as numbers to avoid that annoying warning indication in Excel that value is integer and not string.

 

and the CompareValues are used in OnCompareNodes:

 

Result := CompareValues(ReturnColumnData(Index1, Column), ReturnColumnData(Index2, Column));

 

So I need all in Variants as it seems that all methods that need data already know how to handle Variant types correctly for their purpose - show data, sort (compare) data, export to csv, excel.,, as I tested even sort is fast as before using Variants.

Share this post


Link to post

Hm, so you have different fields for display and data? Like 'Customer Order no' or 'Sum of invoice amounts'... these are all numbers, do you have a display layer where you convert all to text and then show in VTT?

How do you then deal with sorting of numeric columns? Do you keep information about which column data is numeric and sort accordingly (convert text to number and then sort)?

 

Well, me asking you usually means I'm lacking some experience and understanding, but this is really interesting.

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×