Map question

Started by kdmoyers, January 13, 2021, 06:04:22 AM

Previous topic - Next topic

kdmoyers

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)
The mind is everything; What you think, you become.

JTaylor

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

td

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","")".
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

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
The mind is everything; What you think, you become.

td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

Yup.  I think I just sorta confused myself. (chuckle)
-K
The mind is everything; What you think, you become.

td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

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.

td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

disparage is a harsh word; not meant in my post... just asking a question. Appreciate you not taking out of context in the future.


td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

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:

       
  • I'm not the only one who has asked questions about Maps.
  • You have added a MapFind function - I remember one of my posts using Maps (which I do) prompted you to consider a find function.
  • I have several persons I stay in contact with who have WB scripts written as early as 1999. Some of these used VBS dictionary or fabricated recordset to hold associative values. One still works with XP. I make changes for them with no charge if I think something better has come along and most are moving to Win 10.
  • Perhaps my use of the word 'significance' threw you a curve. Probably expected you to reply that Maps are completely internal to WB and alternatives I mentioned involve extra work or components.  That's all.

td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

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

td

A task on the Map todo list is to give extenders the ability to directly create and modify maps. 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor


stanl

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.

JTaylor

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

stanl

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.

JTaylor

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

stanl

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.

JTaylor

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

JTaylor

Not sure if it will though...just thought I would throw it out there.

Jim

stanl

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.

td

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.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

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.