WinBatch® Technical Support Forum

All Things WinBatch => WinBatch => Topic started by: kdmoyers on January 13, 2021, 06:04:22 AM

Title: Map question
Post by: kdmoyers on January 13, 2021, 06:04:22 AM
Why does this program fail? 
Code (winbatch) Select
  a["q"] = "abc"
  a["w"] = mapkeyfind(a,"w","") : "x"

The error is "Variable could not be converted to string"   What variable?
(running version 2020C)
Title: Re: Map question
Post by: JTaylor on January 13, 2021, 08:12:31 AM
This reminds me I need to update.

Probably the MapKeyFind() response.   You are concatenating the response with "x" so it needs to have something it can convert to a string to join with "x".   I have encountered errors like this before when accidentally  trying to display an array rather than an array element.    I wonder if the default value is returning NULL rather than a blank space?   What happens if you change the default value to "z"?

Jim
Title: Re: Map question
Post by: td on January 13, 2021, 09:06:21 AM
Sorry but a bit of comp sci theory is needed to properly explain what is going on...

The problem is the result of how the WIL interpreter evaluates expressions.  The interpreter is an LL2 parser which means that expressions are evaluated from left to right.  The "a["w"]" expression is evaluated when the parser encounters the equal sign (=). That is the "2" part of an LL2 parser.  Because the "a["w"]" expression is creating a map key, the call to MapKeyFind does find the key "w" in the map.  However, because the assignment hasn't finished yet there isn't a value for the key "w".  The statement is equivalent to the statement "a["w"] = a["w"]" which generates a slightly different error.  In other words, WIL needs to allow map keys without values or you couldn't dynamically create new key entries in a map but you can't use the key on the right-hand side of an assignment statement until the key has a value. Without a value, the expression a["w"] evaluates to undefined when it is part of an expression on the right-hand side of an assignment operator.  The colon ( : ) operator encounters the undefined value and issue's the error.

The MapKeyFind function could flag a key with an undefined value as an error when it performs the lookup but that isn't technically an error state because keys with undefined values are allowed in maps. Using the default value when the found key is undefined isn't quite accurate either but that or not changing anything at all may be the best solution. Will have to think about that one a bit.

To answer the specific question the variable is the temporary variable create when a subexpression is evaluated by the parser.  In this case, it is the temporary variable created to hold the result of the evaluations of the subexpression "mapkeyfind(a,"w","")".
Title: Re: Map question
Post by: kdmoyers on January 13, 2021, 02:18:40 PM
Cool, I think I understand.  Boy maps do have their subtleties don't they!

Here what I'll use instead, which is more readable anyway:
Code (winbatch) Select
   if mapkeyexist(a,"w") then a["w"] := "x"
                         else a["w"]  = "x"

Thanks for the help!
-K
Title: Re: Map question
Post by: td on January 13, 2021, 02:30:15 PM
This problem isn't unique to maps.  It is also true of regular WIL arrays.  Consider

Code (winbatch) Select
a[1] = a[1]

or even

Code (winbatch) Select
x = x

It has more to do with dynamic creation of data structures than anything else.
Title: Re: Map question
Post by: kdmoyers on January 13, 2021, 03:29:35 PM
Yup.  I think I just sorta confused myself. (chuckle)
-K
Title: Re: Map question
Post by: td on January 14, 2021, 07:15:32 AM
You raised an interesting question that has resulted in a change to the way the error is handled so it is good that you confused yourself.
Title: Re: Map question
Post by: stanl on January 14, 2021, 09:34:48 AM
Maybe OT: but I'm still on the sidelines about Maps. What is the significance over dictionaries; fabricated recordsets [which can be typed], Powershell hash tables, even Json... all of which exists in WB.
Title: Re: Map question
Post by: td on January 14, 2021, 09:42:58 AM
If their uses are not self-evident to you then don't use them. But please don't disparage those who find them a handy addition to the language.
Title: Re: Map question
Post by: stanl on January 14, 2021, 12:47:59 PM
disparage is a harsh word; not meant in my post... just asking a question. Appreciate you not taking out of context in the future.

Title: Re: Map question
Post by: td on January 15, 2021, 07:40:38 AM
You have asked some version of the same question about maps multiple times and the question has been at least partially answered. At some point, a question becomes more than just a question.  I have no idea of your intentions since I can't read a person's mind but I can certainly understand how the repeated question might be taken in a less than positive way.
Title: Re: Map question
Post by: stanl on January 16, 2021, 04:32:35 AM
Quote from: td on January 15, 2021, 07:40:38 AM
You have asked some version of the same question about maps multiple times and the question has been at least partially answered. At some point, a question becomes more than just a question.  I have no idea of your intentions since I can't read a person's mind but I can certainly understand how the repeated question might be taken in a less than positive way.


Let's clear a few things up:
Title: Re: Map question
Post by: td on January 16, 2021, 08:36:02 AM
I have to admit that you can take some of the credit for the "MapKeyFind" function. One of your posts helped tipped the balance in that direction. - nothing like a mixed metaphor. Part of the justification for adding associative arrays to WIL is the performance advantage of a WIL native implementation.  The "MapKeyFind" function fit nicely into that design target.

Speaking of design, thanks to Kirby's "confusion" some thought is being given to making a small change to the function's behavior.  The change involves having the function return the not-found value when the value of an existing key is undefined instead of the current behavior of throwing an error.  It is a case of pragmatism winning out over pedantry.
Title: Re: Map question
Post by: JTaylor on January 16, 2021, 09:00:26 AM
I am glad you are going that route.  I was going to throw my two cents that direction but figured I didn't know enough to weigh in.  I believe it is what would be "expected" from a user in that situation.

Also, I am glad you added Maps.  It is helpful with some of what I am doing with my Extenders.  I can return Map compatible strings so one can wrap my function within a MapCreate() function and then use a ForEach statement and avoid the Item Extraction, getting a count and the For Loop stuff.   Much cleaner and while not "objects" it comes close to approximating that behavior.

Jim
Title: Re: Map question
Post by: td on January 16, 2021, 09:59:35 AM
A task on the Map todo list is to give extenders the ability to directly create and modify maps. 
Title: Re: Map question
Post by: JTaylor on January 16, 2021, 03:00:11 PM
Excellent.

Jim
Title: Re: Map question
Post by: stanl on January 17, 2021, 05:29:46 AM
Quote from: td on January 16, 2021, 08:36:02 AM
The change involves having the function return the not-found value when the value of an existing key is undefined instead of the current behavior of throwing an error.  It is a case of pragmatism winning out over pedantry.


Hopefully you don't interpret this as another evil question. I'm looking into a more gentle way of converting .NET datasets into SqlLite tables and Jim's extender is top of the charts for this. I will be upgrading to the WB 2021 release and the MapKeyFind() addition seems relevant for replacing the UDF I used when posting during Jim's initial release of his Extender:
Code (WINBATCH) Select


#DefineSubRoutine cvt(colType)
retval = "BLOB"
If colType == "System.Boolean" Then  retval="INTEGER"
If colType == "System.Byte" Then  retval="INTEGER"
If colType == "System.Char" Then  retval="TEXT"
If colType == "System.DateTime" Then retval="TEXT"
If colType == "System.Decimal" Then retval="NUMERIC"
If colType == "System.Int32" Then retval="INTEGER"
If colType == "System.Int64" Then retval="REAL"
If colType == "System.Int16" Then retval="INTEGER"
If colType == "System.Object" Then retval="BLOB"
If colType == "System.Single" Then retval="REAL"
If colType == "System.String" Then retval="TEXT"
If colType == "System.Double" Then retval="REAL"
If colType == "System.Array" Then retval="BLOB"
If colType == "System.UInt16" Then retval="INTEGER"
If colType == "System.UInt32" Then retval="REAL"
If colType == "System.UInt64" Then retval="REAL"
If colType == "System.IntPtr" Then retval="TEXT"




Return(retval)
#EndSubRoutine



Seems the association between .Net and SQlLite type could be easily mapped and a MapKeyFind() more efficient than a series of If's. So my question about the enhancement to the function would be: If a mapped value is not found, could there be an option or parameter to return a default value rather than just 'Not Found".... or maybe that is in the works.
Title: Re: Map question
Post by: JTaylor on January 17, 2021, 05:52:24 AM
If you are using my extender anyway you could create a table with that info and get what you need very easily with the dbQueryValue() function in the extender. 

Jim
Title: Re: Map question
Post by: stanl on January 17, 2021, 07:08:58 AM
Quote from: JTaylor on January 17, 2021, 05:52:24 AM
If you are using my extender anyway you could create a table with that info and get what you need very easily with the dbQueryValue() function in the extender. 

Jim


Right.. create a table, or create a map... that is the qustion.
Title: Re: Map question
Post by: JTaylor on January 17, 2021, 11:29:28 AM
Hard to say not knowing what all you are doing but for easy comparison...Something along these lines (probably with a little tweaking) should get what you need via the Extender.  Also, probably want to grab the latest to make sure it is trimming off unwanted TABs and CRs as well as another minor fix I made.

http://www.jtdata.com/anonymous/wbsql44i.zip

Code (winbatch) Select


dtype = dbQueryValue(db,"SELECT Coalesce(dtype,'BLOB') FROM dtypes WHERE col_type = '%coltype%';")



Jim
Title: Re: Map question
Post by: stanl on January 17, 2021, 01:19:03 PM
Quote from: JTaylor on January 17, 2021, 11:29:28 AM
Hard to say not knowing what all you are doing
Jim


CREATE TABLE, prior to SELECT INTO... matching data types as much as possible.
Title: Re: Map question
Post by: JTaylor on January 17, 2021, 03:44:45 PM
I am sure you are aware of typeof() but in case not, you can do something like the following if that would help simplify things.

SELECT typeof(123), typeof(123.45), typeof('Hello'), typeof('\t') ;

Jim
Title: Re: Map question
Post by: JTaylor on January 17, 2021, 03:57:06 PM
Not sure if it will though...just thought I would throw it out there.

Jim
Title: Re: Map question
Post by: stanl on January 18, 2021, 02:43:13 AM
Jim;


To clear things up. I already have the data types in a .NET DataTable. I map each to an SQLite table with a create statement for the corresponding SQLite data type. In my UDF if a .NET datatype does not map I choose BLOB as the SQLite datatype. I assume the mappings could be performed by a map rather than the UDF IF statements. The question was if MapKeyFind() could accept a default value.


Ok, and I would use similar maps for different db conversions.
Title: Re: Map question
Post by: td on January 19, 2021, 10:03:44 AM
Quote from: stanl on January 17, 2021, 05:29:46 AM

Seems the association between .Net and SQlLite type could be easily mapped and a MapKeyFind() more efficient than a series of If's. So my question about the enhancement to the function would be: If a mapped value is not found, could there be an option or parameter to return a default value rather than just 'Not Found".... or maybe that is in the works.

The function already returns the not-found value (which can be anything you set in the third parameter to the function) when a key is not found in the map.  The proposed change is to return the not-found value when the key is in the map but does not have a defined value. The name of the third parameter is "not-found".  "Not-found" is not the text returned by the function unless you want it to be.
Title: Re: Map question
Post by: stanl on January 19, 2021, 01:04:51 PM
Quote from: td on January 19, 2021, 10:03:44 AM
The function already returns the not-found value (which can be anything you set in the third parameter to the function) when a key is not found in the map. 


Again. asked and answered.