trying to get the data like this from the cfc function - coldfusion

I have a cfc where i need t return the headers for the jqgrid for binding dynamically
i am trying to query the data like this:
colNames: ['ID', 'Institution Name', 'Display Name', 'Short Name', 'Board of Education', 'Scheme Name','Subscription Date'],
colModel: [
{ name: 'institutionid', sortable: true, },
{ name: 'institutionname', sortable: true },
{ name: 'displayname', sortable: true },
{ name: 'shortname' ,sortable: true},
{ name: 'supportedfield', sortable: true },
{ name: 'schemename', sortable: true },
{ name: 'subscriptionto', sortable: true}
]
i can easily get the colNames, but for colmodal how can i bring the elements of sort: true for all by default and format should be like arrayofstructs
Thanks
Query try
<cffunction name="headers" localmode="modern" access="remote" returnformat="json" hint="Handles the Functionality of returning the Table Headers">
<cfset columnsInfos = {}>
<cfset returnArray = []>
<cfset cList = QueryExecute("select top 1 * from mytable").columnList>
<cfset cListQueryObj = QueryNew(cList)>
<cfdump var="#cListQueryObj#" abort>
<cfset colNames = ListtoArray(cList)>
<cfloop query="#cListQueryObj#">
<cfset rowStruct = {}>
<cfloop list="#cList#" index="colname">
<cfset "rowStruct['#colname#']" = cListQueryObj[colname]>
</cfloop>
<cfset arrayAppend(returnArray,rowStruct)>
<cfdump var="#rowStruct#">
</cfloop>
<cfset columnsInfos["colModel"] = returnArray>
<cfset columnsInfos["colNames"] = colNames>
<cfreturn columnsInfos>
</cffunction>

Serializing a query object won't return the expected result. Since you are using the built-in returnFormat="json", the easiest (and probably only) approach is working with an array of structs, just like your JS example shows:
<cffunction name="headers" localmode="modern" access="public" returnformat="json" hint="Handles the Functionality of returning the Table Headers">
<!--- best practise: declare the returned scheme --->
<cfset result = {
"colNames": [],
"colModel": []
}>
<!--- get your table's columns (mockup) --->
<cfset columnList = "institutionid,institutionname,displayname,shortname,supportedfield,schemename,subscriptionto">
<!--- use the column names from your query? --->
<cfset result["colNames"] = listToArray(columnList)>
<!--- add an entry with "name" and "sortable" for every column --->
<cfloop list="#columnList#" index="columnName">
<cfset result["colModel"].add({
"name": columnName,
"sortable": true
})>
</cfloop>
<cfreturn result>
</cffunction>
Note that we are not calling serializeJSON on the result, because returnFormat="json" will already do that for us. Regarding your colNames: Your JS example uses mapped column names, not dynamic ones as shown in your code and the code above. You might want to make them either static or map them yourself.

I like Alex's approach, but I like cfscript better. I also like localized variables.
<cfscript>
/**
* #hint Handles the Functionality of returning the Table Headers
* #output false
* #returnFormat JSON
*/
public any function headers() {
// best practise: declare the returned scheme
var result = {
"colNames": [],
"colModel": []
};
// get your table's columns (mockup)
var columnList = "institutionid,institutionname,displayname,shortname,supportedfield,schemename,subscriptionto";
// use the column names from your query?
result.colNames = listToArray(columnList);
// add an entry with "name" and "sortable" for every column
for (var columnName in columnList) {
result.colModel.add({
"name": columnName,
"sortable": true
});
}
return result;
}
writedump(headers);
writedump(headers());
</cfscript>
Function signature
Function results
Also see
JSON response using cfscript function

Related

coldfusion less cumbersome way to create four structures based on array modulus

I have an array "varray" which needs to be split into four structures. The first of each four elements should be in structure 1, the second in structure 2, etc. I have some working code to do this, but it feels to me like there should be a less cumbersome way. Here is the code:
<cfset xord = StructNew()>
<cfset xsort = StructNew()>
<cfset xsel = StructNew()>
<cfset xmer = StructNew()>
<cfloop from = '1' to = "#ArrayLen(varray)#" index = 'i'>
<cfset fieldname = farray[i]> <!---farray previously defined --->
<cfset val = varray[i]> <!---varray previously defined --->
<cfset j = i%4>
<cfif j EQ 1>
<cfset xord[fieldname] = val>
<cfselseif j EQ 2>
<cfset xsort[fieldname]= val>
<cfelseif j EQ 3>
<cfset xsel[fieldname] = val>
<cfelseif j EQ 0>
<cfset xmer[fieldname] = val>
</cfif>
</cfloop>
Can anyone suggest a better way to do this?
It's been ages i did some CF, but a tag based approach by making use of local scope:
<cfset keys = ['xord', 'xsort', 'xsel', 'xmer'] />
<cfset farray = ['f1','f2','f3','f4','f5','f6','f7','f8']>
<cfset varray = ['v1','v2','v3','v4','v5','v6','v7','v8']>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset local[keys[i%4+1]][farray[i]] = varray[i]>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
Now you have xord, xsort, xsel and xmer filled with the right key-value pairs within your local scope.
How about cfscript?
<cfscript>
function groupByOp(values, fieldnames) {
var ops = ['mer', 'ord', 'sort', 'sel'];
var byOp = {};
arrayEach(values, function (val, i) {
byOp["x#ops[i % 4 + 1]#"][fieldnames[i]] = val;
});
return byOp;
}
</cfscript>
This makes use of the fact that CF will automagically create structs when you mention a non-existing member.
Test:
<cfset v = ListToArray('1,2,3,4,5,6')>
<cfset f = ListToArray('a,b,c,d,e,f')>
<cfoutput>
<pre>#SerializeJSON(groupByOp(v, f))#</pre>
</cfoutput>
outputs
{
"xsel": {
"c": "3"
},
"xord": {
"e": "5",
"a": "1"
},
"xsort": {
"b": "2",
"f": "6"
},
"xmer": {
"d": "4"
}
}
(You didn't mention your version, so I don't know if you have access to newer functions like array each(). Keep in mind there's slicker options in newer versions)
Instead of creating separate variables, create a single structure containing the 4 variables, and an array of names. Then use the array and MOD to populate the substructures. Note, the example below creates the subsubstructures up front to ensure they always exist - even if the field/value arrays are empty or contain less than 4 elements.
TryCF.com Example
<cfset farray = ["A","B","C","D","E","F","G","Q","T"]>
<cfset varray = ["11","22","33","RR","55","NN","77","68","46"]>
<cfset data = {xOrd={},xSort={},xSel={},xMer={}}>
<cfset names = ["xOrd","xSort","xSel","xMer"]>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset fieldName = farray[i]>
<cfset fieldValue = varray[i]>
<cfset structName = names[i%4+1]>
<cfset data[structName][fieldName] = fieldValue>
</cfloop>
The substructures can be accessed through the parent, data.
<cfdump var="#data.xOrd#" label="data.xOrd">
<cfdump var="#data.xSort#" label="data.xSort">
<cfdump var="#data.xSel#" label="data.xSel">
<cfdump var="#data.xMer#" label="data.xMer">
Another couple of approaches.
I wouldn't necessarily say that these are better, but just different:
Requires field array length to match value array length
<cfset farray = ['field1','field2','field3','field4','field5']>
<cfset varray = ['apple','orange','pear','kiwi','pineapple']>
<cfset xord = {}>
<cfset xsort = {}>
<cfset xsel = {}>
<cfset xmer = {}>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset j =
((i%4 EQ 1) ? (StructInsert(xord,farray[i],varray[i])):
((i%4 EQ 2) ? (StructInsert(xsort,farray[i],varray[i])):
((i%4 EQ 3) ? (StructInsert(xsel,farray[i],varray[i])):
((i%4 EQ 0) ? (StructInsert(xmer,farray[i],varray[i])): 0))))>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
OR:
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfswitch expression="#i%4#">
<cfcase value="0">
<cfset xmer[farray[i]] = varray[i]>
</cfcase>
<cfcase value="1">
<cfset xord[farray[i]] = varray[i]>
</cfcase>
<cfcase value="2">
<cfset xsort[farray[i]] = varray[i]>
</cfcase>
<cfcase value="3">
<cfset xsel[farray[i]] = varray[i]>
</cfcase>
</cfswitch>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
Field array length does not have to match value array length
<cfset farray = ['field1','field2','field3','field4']>
<cfset varray = ['apple','orange','pear','kiwi','pineapple']>
<cfset xord = {}>
<cfset xsort = {}>
<cfset xsel = {}>
<cfset xmer = {}>
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfset j =
((i%4 EQ 1 AND ArrayIsDefined(farray,i)) ? (StructInsert(xord,farray[i],varray[i],true)) :
((i%4 EQ 2 AND ArrayIsDefined(farray,i)) ? (StructInsert(xsort,farray[i],varray[i],true)) :
((i%4 EQ 3 AND ArrayIsDefined(farray,i)) ? (StructInsert(xsel,farray[i],varray[i],true)) :
((i%4 EQ 0 AND ArrayIsDefined(farray,i)) ? (StructInsert(xmer,farray[i],varray[i],true)) : 0))))>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />
OR:
<cfloop from="1" to="#ArrayLen(varray)#" index="i">
<cfswitch expression="#i%4#">
<cfcase value="0">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xmer,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="1">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xord,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="2">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xsort,farray[i],varray[i],true)) : 0)>
</cfcase>
<cfcase value="3">
<cfset (ArrayIsDefined(farray,i) ? (StructInsert(xsel,farray[i],varray[i],true)) : 0)>
</cfcase>
</cfswitch>
</cfloop>
<cfdump var="#xord#" />
<cfdump var="#xsort#" />
<cfdump var="#xsel#" />
<cfdump var="#xmer#" />

Custom function works in ColdFusion 10/11 but throws error in Lucee

I have the following function, inherited from some CF code. It works, but only in ColdFusion version 10 and 11. I am not concerned with 2016 as no plans for upgrade. I'm trying to run it under Lucee, but it is not working.
<cffunction name="QueryToArray" access="public" returntype="array" output="false" hint="This turns a query into an array of structures.">
<!--- Define arguments. --->
<cfargument name="Data" type="query" required="yes" />
<cfscript>
var LOCAL = StructNew(); // Define the local scope.
LOCAL.Columns = data.getMetaData().getColumnLabels(); // Get the column names as an array.
LOCAL.QueryArray = ArrayNew(1); // Create an array that will hold the query equivalent.
for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount;
LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
LOCAL.Row = StructNew();
for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns);
LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];
LOCAL.Row[LOCAL.ColumnName] = ARGUMENTS.Data[LOCAL.ColumnName][LOCAL.RowIndex];
}
ArrayAppend(LOCAL.QueryArray, LOCAL.Row);
}
return(LOCAL.QueryArray);
</cfscript>
</cffunction>
In Lucee, I tried making these changes:
<cffunction name="QueryToArray" access="public" returntype="array" output="false" hint="This turns a query into an array of structures.">
<!--- Define arguments. --->
<cfargument name="Data" type="query" required="yes" />
<cfscript>
var LOCAL = StructNew(); // Define the local scope.
LOCAL.Columns = data.getColumnlist(false); // Get the column names as an array.
LOCAL.QueryArray = ArrayNew(1); // Create an array that will hold the query equivalent.
for (LOCAL.RowIndex = 1 ; LOCAL.RowIndex LTE ARGUMENTS.Data.RecordCount;
LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
LOCAL.Row = StructNew();
for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns);
LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];
LOCAL.Row[LOCAL.ColumnName] = ARGUMENTS.Data[LOCAL.ColumnName][LOCAL.RowIndex];
}
ArrayAppend(LOCAL.QueryArray, LOCAL.Row);
}
return(LOCAL.QueryArray);
</cfscript>
</cffunction>
but I'm getting this error:
Message: Can't cast String [name,value] to a value of type [Array]
Detail: Java type of the object is java.lang.String
Stacktrace:
The Error Occurred in
C:\home\website\wwwroot\controller\production\cfc\kernel.cfc: line 12
10: LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
11: LOCAL.Row = StructNew();
12: for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE ArrayLen(LOCAL.Columns);
13: LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
14: LOCAL.ColumnName = LOCAL.Columns[LOCAL.ColumnIndex];
To get your code to work, all you need to do is change line 6 from
LOCAL.Columns = data.getColumnlist(false); // Get the column names as an array.
to this
LOCAL.Columns = data.getColumnNames(); // Get the column names as an array.
However I found a gist here that works in both ColdFusion and Lucee. I decided to create some sample code and test it at https://trycf.com/. I verified that it works in Lucee 4.5 and 5. It also works in ColdFusion 10, 11, 2016 as well. Hope this helps!
<!--- Create a new three-column query, specifying the column data types --->
<cfset myQuery = QueryNew("Name, Time, Advanced", "VarChar, Time, Bit")>
<!--- Make two rows in the query --->
<cfset QueryAddRow(MyQuery, 2)>
<!--- Set the values of the cells in the query --->
<cfset QuerySetCell(myQuery, "Name", "The Wonderful World of CMFL", 1)>
<cfset QuerySetCell(myQuery, "Time", "9:15 AM", 1)>
<cfset QuerySetCell(myQuery, "Advanced", False, 1)>
<cfset QuerySetCell(myQuery, "Name", "CFCs for Enterprise
Applications", 2)>
<cfset QuerySetCell(myQuery, "Time", "12:15 PM", 2)>
<cfset QuerySetCell(myQuery, "Advanced", True, 2)>
<h4>The query</h4>
<cfdump var="#myQuery#">
<h4>The array of objects</h4>
<cfset myArray = QueryToArray(myQuery)>
<cfdump var="#myArray#">
<cfscript>
/**
* #hint Returns reasonable array of objects from a cfquery
*/
public function queryToArray(
required query query,
string excludeColumns = ""
){
if (server.coldfusion.productName == "ColdFusion Server") {
local.columns = arguments.query.getMetaData().getColumnLabels();
} else if (server.coldfusion.productName == "Lucee") {
local.columns = arguments.query.getColumnNames();
} else {
local.columns = [];
}
local.response = arrayNew(1);
for (local.rowIndex=1; local.rowIndex<=arguments.query.recordCount; local.rowIndex++) {
local.response[local.rowIndex] = structNew();
for (local.columnIndex=1; local.columnIndex<=arrayLen(local.columns); local.columnIndex++) {
local.columnName = local.columns[local.columnIndex];
if(local.columnName != "" && (arguments.excludeColumns == "" || !listFindNoCase(arguments.excludeColumns, local.columnName))) {
local.response[local.rowIndex][local.columnName] = arguments.query[local.columnName][local.rowIndex];
}
}
}
return local.response;
}
</cfscript>

Stumped by list, string manipulation issue

Given the following list (#oldList#):
category1_item1, category1_item2, category2_item1, category3_item1, category3_item2"
How can I create the following list (#newList#)?:
category1[item1&item2],category2[item1],category3[item1&item2]?
This is what I have so far:
<cfset newList = "">
<cfset category = "">
<cfloop list="#oldList#" index="listElement">
<cfset endPos = find("_", listElement)>
<cfset listElementCategory = left(listElement, endPos)>
<cfset listElementItem = replace(listElement, listElementCategory, "")>
<cfif listElementCategory is not category>
<cfset modifiedElement = replace(listElementCategory, "_", "") & "[" & listElementItem>
<cfelse>
<cfset modifiedElement = "&" & listElementItem>
</cfif>
<cfset category = listElementCategory>
<cfset newList = newList & modifiedElement>
</cfloop>
This code results in:
category1[item1&item2category2[item1category3[item1&item2
I just can't figure out how to close each "grouping" of items with "],".
<cfset newList = "">
<cfset category = "">
<cfloop list="#oldList#" index="listElement">
<cfset endPos = find("_", listElement)>
<cfset listElementCategory = left(listElement, endPos)>
<cfset listElementItem = replace(listElement, listElementCategory, "")>
<cfif listElementCategory is not category>
<cfif category is not "">
<!--- category has changed and this isn't the first record, so close previous category --->
<cfset newList = newList & "],">
</cfif>
<cfset modifiedElement = replace(listElementCategory, "_", "") & "[" & listElementItem>
<cfelse>
<cfset modifiedElement = "&" & listElementItem>
</cfif>
<cfset category = listElementCategory>
<cfset newList = newList & modifiedElement>
</cfloop>
Note I just added this block:
<cfif category is not "">
<!--- category has changed and this isn't the first record, so close previous category --->
<cfset newList = newList & "],">
</cfif>
Edit:
Almost forgot the end of the loop! After the </cfloop> close the brackets like:
<cfif category is not "">
<!--- close the final bracket since we have at least one record --->
<cfset newList = newList & "]">
</cfif>

structure within an array cell coldfusion

I've been trying to create a data structure, but I'm having a hard time. I am trying to create a data structure like this:
{
"vehicle": [
{
"inv_id": "123412",
"year": "2013",
"make": "Jeep",
"model": "Grand Cherokee"
},
{
"inv_id": "1224522",
"year": "2013",
"make": "Jeep",
"model": "Grand Cherokee"
}
]
}
Here is what I've tried with no luck.
<cfset result["vehicle"] = []>
<cfoutput>
<cfloop query="qinv">
#arrayAppend("result.vehicle,{})#
<cfloop array="#result.vehicle#" index="i">
#structInsert(result.vehicle[i], "inventory_id", qInv.inventory_id)#
#structInsert(result.vehicle[i], "year", qInv.year)#
#structInsert(result.vehicle[i], "make", qInv.make)#
#structInsert(result.vehicle[i], "model", qInv.model)#
</cfloop>
</cfloop>
</cfoutput>
This is throwing a coldfusion error, The value coldfusion.runtime.Struct cannot be converted to a number. on the first structInsert line.
Any suggestions?
You don't need that array loop... think about it: what are you looping over? It's an empty array. All you need to do is append the struct to the array:
<cfset arrayAppend( result.vehicle,{
"inventory_id" = qInv.inventory_id,
"year" = qInv.year,
"make" = qInv.make,
"model" = qInv.model
})>

JSON contains Special Characters

My JSON contains special characters like: new line, double quotes etc.
I am creating the JSON using Coldfusion server side script. But in case of special chars I get error due to wrongly formatted JSON. What should I do in such a case?
<cfoutput>
[
<cfset loopIndex=0>
<cfloop query="qEvents">
<cfif loopIndex NEQ 0>,</cfif>
<cfif is_allday EQ 1>
<cfset isallDayEvent = "true">
<cfelse>
<cfset isallDayEvent = "false">
</cfif>
{
"title": "#title#",
"start": "#DateFormat(start_date_time,'mm/dd/yyyy')# #TimeFormat(start_date_time,'hh:mm tt')#",
"end": "#DateFormat(end_date_time,'mm/dd/yyyy')# #TimeFormat(end_date_time,'hh:mm tt')#",
"allDay": #isallDayEvent#,
"eventID": "#event_id#",
"duration": "#duration#",
"note": "#note#",
"location": "#location#"
}
<cfset loopIndex=loopIndex+1>
</cfloop>
]
</cfoutput>
Rather than writing the JSON by hand, you should generate an array of structs and then use serializeJSON() to convert it to a valid JSON string:
<cfset thisArrayBecomesJSON = [] />
<cfloop query="qEvents">
<cfif is_allday EQ 1>
<cfset isAllDayEvent = "true" />
<cfelse>
<cfset isAllDayEvent = "false" />
</cfif>
<cfset thisEvent = {
'title' = title,
'start' = dateFormat( start_date_time, 'mm/dd/yyyy' ) & timeFormat( start_date_time, 'hh:mm tt' ),
'end' = dateFormat( end_date_time, 'mm/dd/yyyy' ) & timeFormat( end_date_time, 'hh:mm tt' ),
'allDay' = isAllDayEvent,
'eventID' = event_id,
'duration' = duration,
'note' = note,
'location' = location
} />
<cfset arrayAppend( thisArrayBecomesJSON, thisEvent ) />
</cfloop>
<cfset myJSON = serializeJSON( thisArrayBecomesJSON ) />
<cfoutput>#myJSON#</cfoutput>
This is untested, but I think it should work ok - there may be some syntax errors.