ColdFusion loop through struct with key evaluation fails! What am i missing? - data-structures

I have this code in my cfm, which works
<cfif not StructIsEmpty(form)>
<cfset larray = user.getArray() />
<cfloop collection="#form#" item="key">
<cfif left(key,4) eq "UPD_">
<cfset x = listLast(key,"_") />
<cfset y = evaluate(0,key) />
<cfloop index="j" from="1" to="#arrayLen(larray)#">
<cfif (larray[j][1] eq x) and (larray[j][3] neq y)>
<cfset larray[j][3] = y />
<cfif not LSIsNumeric(larray[j][3])>
<cfset larray[j][3] = "0" />
</cfif>
<cfset larray[j][4] = "update" />
</cfif>
</cfloop>
</cfif>
</cfloop>
<cfloop collection="#form#" item="key">
<cfif left(key,4) eq "DEL_">
<cfset x = listLast(key,"_") />
<cfloop index="k" from="1" to="#arrayLen(larray)#">
<cfif larray[k][1] eq x>
<cfset larray[k][4] = "delete" />
</cfif>
</cfloop>
</cfif>
</cfloop>
<cfset user = createObject("component", "cfc.User").init(
identifier = FormatBaseN(form.id,10),
array = larray
) />
</cfif>
<form name="usform" method="POST">
<cfset array = user.getArray() />
<cfoutput>
<cfloop index="i" from="1" to="#arrayLen(array)#">
<table>
<tr>
<td><input type="text" name="upd_#array[i][1]#" maxlength="6" size="6" value="#array[i][3]#" /></td>
<td><input type="checkbox" name="del_#array[i][1]#" /></td>
</tr>
</table>
<input type="hidden" name="id" value="#user.getIdentifier()#" />
</cfoutput>
</form>
I have put it into a cfc to seperate my logic and my view and i am trying to make it more generic
<cfcomponent name="ArrayManager" output="false">
<cffunction name="init" hint="constructor" output="false" returntype="ArrayManager">
<cfargument name="user" type="User" required="true" hint="User bean" />
<cfargument name="form" type="Struct" required="true" />
<cfset variables.instance.array = arguments.user.getArray() />
<cfset variables.instance.form = arguments.form />
<cfreturn this />
</cffunction>
<cffunction name="update" access="public" output="true" returntype="boolean">
<cfargument name="structstring" type="String" required="true" />
<cfargument name="seperator" type="String" required="true" />
<cfset var x = "0" />
<cfset var y = "0" />
<cfloop collection="#variables.instance.form#" item="key">
<cfif key eq "#arguments.structstring#">
<cfset x = listLast(key,"#arguments.seperator#") />
<cfset y = evaluate(0,key) />
<cfloop index="j" from="1" to="#arrayLen(variables.instance.array)#">
<cfif (variables.instance.array[j][1] eq x) and (variables.instance.array[j][3] neq y)>
<cfset variables.instance.array[j][3] = y />
<cfif not LSIsNumeric(variables.instance.array[j][3])>
<cfset variables.instance.array[j][3] = "0" />
</cfif>
<cfset variables.instance.array[j][4] = "update" />
</cfif>
</cfloop>
</cfif>
</cfloop>
<cfset arguments.user.init(array = variables.instance.array) />
<cfreturn true />
</cffunction>
<cffunction name="delete" access="public" output="false" returntype="boolean">
<cfargument name="structstring" type="String" required="true" />
<cfargument name="seperator" type="String" required="true" />
<cfset var x = "0" />
<cfloop collection="#variables.instance.form#" item="key">
<cfif key eq "#arguments.structstring#">
<cfset x = listLast(key,"#arguments.seperator#") />
<cfloop index="k" from="1" to="#arrayLen(variables.instance.array)#">
<cfif variables.instance.array[k][1] eq x>
<cfset variables.instance.array[k][4] = "delete" />
</cfif>
</cfloop>
</cfif>
</cfloop>
<cfset arguments.user.init(array = variables.instance.array) />
<cfreturn true />
</cffunction>
</cfcomponent>
And my new cfm
<cfif not StructIsEmpty(form)>
<cfset arraymanager = createObject("component","cfc.ArrayManager").init(user,form) />
<cfset seperator = "_" />
<cfset structstring = "UPD" />
<cfset arraymanager.update(structstring,seperator) />
</cfif>
...
It fails, i get this error message
The CFML compiler encountered an unexpected coldfusion.compiler.CompilerInternalException exception.
The reason for this was: Unable to complete CFML to Java translation. Occurred at:
.
.
.
The error occurred in C:\path\to\document\root\cfc\ArrayManager.cfc: line 21
Called from C:\path\to\document\root\cfc\update-emp.cfm: line 66
Called from C:\C:\path\to\document\root\cfc\update-emp.cfm: line 66
19 : <cfif key eq "#arguments.structstring#">
20:
21 : <cfset y = evaluate(0,key) />
22:
23 `:
What am i doing wrong or is there a better way to accomplish what i am trying to do (showing database content in a table and updating(Update and Delete) the database content through the same table)

The error message you've posted indicates that you're misusing the Evaluate function. According to the docs, it works like this:
Evaluates one or more string expressions, dynamically, from left to right. (The results of an evaluation on the left can have meaning in an expression to the right.) Returns the result of evaluating the rightmost expression.
But you've got other problems, too. For starters, you're not duplicating the logic correctly when you move the code into the CFC.
In your working code, you use the conditional:
<cfif left(key,4) eq "UPD_">
But in your CFC you have:
<cfif key eq arguments.structString>
This should be:
<cfif left(key,4) eq arguments.structString>
Next, you aren't using the best syntax for evaluate, and you probably don't want to use it at all. The statement:
y = evaluate(0,key)
can be rewritten as:
y = evaluate(key)
Since the value of key is "UPD_something", this can be rewritten as:
y = [variables|arguments|etc].UPD_Something
(Since you're not explicitly specifying a variable scope, CF will attempt to find the variable in a set of scopes in a certain order; which is why I used the syntax [a|b|...])
You probably don't mean this, you probably want the value from the form. And since the key name is dynamic, you should access it this way (instead of using evaluate):
y = variables.instance.form[key]
I think that may fix it. So, to summarize:
Replace your statement <cfif key eq arguments.structString> with <cfif left(key,4) eq arguments.structString> (And make sure that the value you pass as "structString" includes the underscore!)
Replace your use of evaluate with: y = variables.instance.form[key]
I hope that fixes your problems...
After you get it working, start thinking about your variable names. "Array" is a terrible variable name because it is practically a reserved word in CFML. And using "x" and "y" is not descriptive at all. These types of problems are what made this question hard to answer.

I fully agree to Adam Tuttle's post. I've removed the "solution part" part of my answer in favor of his. So here's my two cents regarding the "general part":
Avoiding Evaluate() altogether is the best thing you can do. There is no reason to use it apart from actually evaluating pieces of code (which is another bag of hurt that should be avoided). If that's not what you are doing, then there is no situation that could not be resolved through something more appropriate than Evaluate(), e.g.:
<cfset foo = Evaluate("FORM.#foo#")>
is equal to:
<cfset foo = FORM[foo]>
All of the "conveniency" misuses of Evaluate() can be addressed like this.
A few more tips:
Avoid meaningless variable names like "x" or "y" (unless you refer to 2D-coordinates, of course).
Don't do "#variable#" - you can simply use variable instead.
Use Structs wherever you can when you need to access items with a key, e.g.
<cfif array[i][1] eq SomeValue> is a lot less elegant than
<cfif array[i]["FieldName"] eq SomeValue>
You don't really need "variables.instance" - every component instance has it's own VARIABLES scope. Whatever you stick in there is "instance-only" by default.
No need to pass in the FORM scope to a component. The scope is global, so CFCs can see it anyway.
Why do you store database stuff in an extra array instead of using the Query object you got when you retrieved them?
No need to self-close ("/>") CFML statements - you are not writing XML (though that's a matter of taste).

Related

How can I use XPath to find the minimum/maximum value of an attribute for chooses group in a set of elements?

<foo>
<bar id="1" score="100" group="beginner" />
<bar id="2" score="200" group="beginner" />
<bar id="3" score="300" group="expert" />
...
</foo>
I try use like this, but something wrong (xpath 1.0)
foo/bar[#group='beginner' and not(#score<= preceding-sibling::bar/#score) and not(#score<=following-sibling::bar/#score)]
using xpath 1.0
/foo/bar[#group='beginner'][(not(preceding-sibling::bar[#group='beginner']/#score >= #score) and not(following-sibling::bar[#group='beginner']/#score > #score)) or (not(preceding-sibling::bar[#group='beginner']/#score <= #score) and not(following-sibling::bar[#group='beginner']/#score < #score))]/#score

Is it possible to identify multiple values in a WHENNOT rule when defining WIT object?

We are attempting to expose a new field to only two groups (of many) within a TFS team project. We would like to implement a rule to the WIT object for which multiple values would satisfy the issue.
We have tried nested..
<WHENNOT field="System.AreaId" value="1"/>
<WHENNOT field="System.AreaId" value="2"/>
repeated...
<WHENNOT field="System.AreaId" value="1">
</WHENNOT>
<WHENNOT field="System.AreaId" value="2">
</WHENNOT>
and some guesses to the syntax..
<WHENNOT field="System.AreaId" value="1 or 2"/>
<WHENNOT field="System.AreaId" value="1 || 2"/>
<WHENNOT field="System.AreaId" value="1, 2"/>
without achieving the intended result.
<FIELD name="Original Estimate" refname="Microsoft.VSTS.Scheduling.OriginalEstimate" type="Double" reportable="measure" formula="sum">
<HELPTEXT>Initial value for Remaining Work - set once, when work begins</HELPTEXT>
<WHENNOT field="System.AreaId" value="24">
<READONLY />
</WHENNOT>
<WHENNOT field="System.State" value="To Do">
<READONLY />
</WHENNOT>
<WHEN field="System.State" value="Done">
<REQUIRED />
</WHEN>
</FIELD>
We would like to show the field based on whether the WIT is associated with Area ID 1 or 2, but otherwise hide from all of others.
You can't combine 2 WHENNOT because they cancel each other.
You need to use <WHEN field="System.AreaId" value="{id}"> <READONLY> </WHEN>
with all the areas (unless 1 and 2).

Summary table with xForms

I have an xml like the following:
<table1>
<row>
<person>person1</person>
<value>10</value>
</row>
<row>
<person>person2</person>
<value>20</value>
</row>
<row>
<person>person1</person>
<value>5</value>
</row>
</table1>
<summaryTable>
<row>
<person>person1</person>
<value_total/>
</row>
<row>
<person>person2</person>
<value_total/>
</row>
</summaryTable>
With XForms 1 (there is no option to switch to XForms 2), using framework betterform, I want to calculate the values in the summary table, by doing the SUM of the rows in 'table1' that have the same person name. To do that I have the following binds:
<xf:bind id="bind_table1"
nodeset="table1" repeatableElement="row">
<xf:bind id="bind_head_table1" nodeset="head" />
<xf:bind id="bind_row_table1" nodeset="row">
<xf:bind id="bind_person" nodeset="person" type="xf:string" />
<xf:bind id="bind_value" nodeset="value" type="xf:integer" />
</xf:bind>
</xf:bind>
<xf:bind id="bind_summaryTable"
nodeset="summaryTable"
repeatableElement="row">
<xf:bind id="bind_head_summaryTable" nodeset="head" />
<xf:bind id="bind_row_summaryTable" nodeset="row">
<xf:bind id="bind_person_name" nodeset="person_name" type="xf:string" readonly="true"/>
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = ../person_name/text()]/value)"/>
</xf:bind>
</xf:bind>
What I want to have at the end is the value_total for person1 = 15 and value_total for person2 = 20, but using this 'calculate' expression I'm getting 'NaN'. If I replace the calculate expression to compare with a literal String like:
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = 'person1']/value)"/>
then I get as value_total 15 (the sum is correctly done). So it seems that the error is in the comparison expression person/text() = ../person_name/text() . Does someone have an idea about how should be the correct expression?
Thanks
Try the context() function in the calculate attribute to refer to the current node, like this:
<xf:bind nodeset="summaryTable/row/value_total" calculate="sum(//table1/row[person/text() = context()/../person/text()]/value)"/>
The context function gives you the current context node. If your bind references a nodeset with multiple nodes, it will be evaluated one time for every node, and that node is what context() returns.
It works for me with XSLTForms, maybe your version of betterForm supports it.

How to insert Identity value to another table in SQLXML

I am using SQLXML 4.0 to Bulkload data from the XML to the SQL Database. I have 2 tables. "Country" and "Customer". Below is my XML file format ("Data.xml")
<Root>
<Countries>
<Country Name="USA"></Country>
<Country Name="Australia"></Country>
</Countries>
<Customers>
<Customer Name="John Smith" CountryName="Australia"></Customer>
</Customers>
</Root>
I have 2 tables
Country
Id Name
1 USA
2 Australia
Customer
Id CustomerName CountryId
1 John Smith 2
When i import my XML, "Id" column in the country is auto generated as it is identity(1,1). I want this "Id" value to sit in my Customer Table "CountryId" Column.
Note that I cannot nest the Customer Tag within the Country Tag. By nesting, i can easily define Parent and Child key in the XSD. But without nesting the elements i have not found a way to define these relationships.
Below is my XSD (Schema.xml)
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:appinfo>
<sql:relationship name="Country_Customer"
parent="Country"
parent-key="Id"
child="Customer"
child-key="CountryId" />
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="Root" sql:is-constant="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Countries" sql:is-constant="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="unbounded" name="Country" sql:relation="Country">
<xsd:complexType>
<xsd:attribute name="Name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Customers" sql:is-constant="1">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Customer" sql:relation="Customer" sql:relationship="Country_Customer">
<xsd:complexType>
<xsd:attribute name="Name" type="xsd:string" use="required" sql:field="CustomerName" />
<xsd:attribute name="CountryName" type="xsd:string" use="required" sql:field="CountryId" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
I have defined an SQL relationship but i am specifying the Country Name in my XML and hence the relationship does not work.
Below are the table creation scripts
CREATE TABLE [dbo].[Country](
[Id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Name] [nvarchar](50) NULL,
)
CREATE TABLE [dbo].[Customer](
[Id] [bigint] IDENTITY(1,1) NOT NULL Primary Key,
[CustomerName] [nvarchar](50) NULL,
[CountryId] [bigint] NULL,
)
I am using the below VB Script to bulk import
Dim FileValid
set objBL = CreateObject("SQLXMLBulkLoad.SQLXMLBulkload.4.0")
objBL.ConnectionString = "provider=SQLOLEDB;data source=ServerName;database=databasename;User Id=username;Password=password"
objBL.ErrorLogFile = "c:\error.log"
objBL.KeepIdentity = False
'Validate the data file prior to bulkload
Dim sOutput
sOutput = ValidateFile("Data.xml", "", "Schema.xml")
WScript.Echo sOutput
If FileValid Then
' Check constraints and initiate transaction (if needed)
' objBL.CheckConstraints = True
' objBL.Transaction=True
'Execute XML bulkload using file.
objBL.Execute "Schema.xml", "Data.xml"
set objBL=Nothing
End If
Function ValidateFile(strXmlFile,strUrn,strXsdFile)
' Create a schema cache and add SampleSchema.xml to it.
Dim xs, fso, sAppPath
Set fso = CreateObject("Scripting.FileSystemObject")
Set xs = CreateObject("MSXML2.XMLSchemaCache.6.0")
sAppPath = fso.GetFolder(".")
xs.Add strUrn, sAppPath & "\" & strXsdFile
' Create an XML DOMDocument object.
Dim xd
Set xd = CreateObject("MSXML2.DOMDocument.6.0")
' Assign the schema cache to the DOM document.
' schemas collection.
Set xd.schemas = xs
' Load XML document as DOM document.
xd.async = False
xd.Load sAppPath & "\" & strXmlFile
' Return validation results in message to the user.
If xd.parseError.errorCode <> 0 Then
ValidateFile = "Validation failed on " & _
strXmlFile & vbCrLf & _
"=====================" & vbCrLf & _
"Reason: " & xd.parseError.reason & _
vbCrLf & "Source: " & _
xd.parseError.srcText & _
vbCrLf & "Line: " & _
xd.parseError.Line & vbCrLf
FileValid = False
Else
ValidateFile = "Validation succeeded for " & _
strXmlFile & vbCrLf & _
"======================" & _
vbCrLf & "Contents to be bulkloaded" & vbCrLf
FileValid = True
End If
End Function
Hope this may help: SQL Server XML

What's wrong with this mergesort?

I'm trying to implement mergesort in Coldfusion, but it is spitting out incorrect results, code:
<cffunction name="mergeSort" hint="Sorts arrays of structs">
<cfargument name="arr" type="Array" required="yes">
<cfif Arraylen(arr) LTE 1>
<cfreturn arr />
</cfif>
<cfset left_ = ArrayNew(1)>
<cfset right_ = ArrayNew(1)>
<cfset mid_ = Int(Arraylen(arr) / 2)>
<cfloop index="i" from="1" to="#mid_#">
<cfset arrayAppend(left_, arr[i])>
</cfloop>
<cfloop index="j" from="#mid_+1#" to="#ArrayLen(arr)#">
<cfset arrayAppend(right_, arr[j])>
</cfloop>
<cfreturn merge( mergeSort(left_), mergeSort(right_) )>
</cffunction>
<cffunction name="merge" hint="Merges two arrays">
<cfargument name="left_" required="yes" type="Array">
<cfargument name="right_" required="yes" type="Array">
<cfset result = ArrayNew(1)>
<cfloop condition="ArrayLen(left_) GT 0 AND ArrayLen(right_) GT 0">
<cfif left_[1].attr3 LTE right_[1].attr3>
<cfset arrayAppend(result, left_[1])>
<cfset arrayDeleteAt(left_, 1)>
<cfelse>
<cfset arrayAppend(result, right_[1])>
<cfset arrayDeleteAt(right_, 1)>
</cfif>
</cfloop>
<cfif ArrayLen(left_) GT 0>
<cfloop array="#left_#" index="v">
<cfset ArrayAppend(result, v)>
</cfloop>
</cfif>
<cfif ArrayLen(right_) GT 0>
<cfloop array="#right_#" index="v">
<cfset ArrayAppend(result, v)>
</cfloop>
</cfif>
<cfreturn result />
</cffunction>
It's sorting an array of structs, on the struct key called "attr3". What happens is that it splits the lists correctly, it seems, but then continues to attach the same list to the resultset. So, for example, if I have left_.attr3 as "Title" and right_.attr3 as "Another", the result ends up being "Title", "Another", "Another", "Another" .. etc.
Have you looked at How to sort an array of structs in ColdFusion ?
btw, pls var scope all your variables!
btw, you can join arrays with Java Join Two Arrays in ColdFusion
btw, you can use mid_ = Arraylen(arr) \ 2 for integer div

Resources