I have two separate lists which are giving me the results, I want to convert those separate lists in one query, i did used listoquery from cflib , but it does only for 1, not for more queries.
How can i do it, any clue, can't use queryappend, because i am on lucee.
<cffunction name="listToQuery" access="public" returntype="query" output="false"
hint="Converts a list to a single-column query.">
<cfargument name="list" type="string" required="yes" hint="List to convert.">
<cfargument name="delimiters" type="string" required="no" default="," hint="Things that separate list elements.">
<cfargument name="column_name" type="string" required="no" default="column" hint="Name to give query column.">
<cfset var query = queryNew(arguments.column_name)>
<cfset var index = ''>
<cfloop list="#arguments.list#" index="index" delimiters="#arguments.delimiters#">
<cfset queryAddRow(query)>
<cfset querySetCell(query,arguments.column_name,index)>
</cfloop>
<cfreturn query>
</cffunction>
or i have a for loop
like this
https://trycf.com/gist/28e9f2b8ff2992e0dc9f78709a0d2041/lucee5?theme=monokai
<cfscript>
list1 = '1,2,3,4,5,6,7,8';
list2 = '5,5,5,5,7,7,4,4';
result = queryNew("");
queryAddListAsNewColumn(result, "id", list1);
queryAddListAsNewColumn(result, "name", list1);
queryAddListAsNewColumn(result, "cid", list2);
writeDump(result);
/*
* Adds the provided list to the query as a new column. Automatically expands query records.
*/
function queryAddListAsNewColumn(query, columnName, list) {
// prepare list data
LOCAL.data = listToArray(ARGUMENTS.list);
LOCAL.dataCount = arrayLen(LOCAL.data);
// add new column to query
queryAddColumn(ARGUMENTS.query, ARGUMENTS.columnName);
// expand query if list to add exceeds the current record count
LOCAL.rowDelta = (LOCAL.dataCount - ARGUMENTS.query.recordCount);
if (LOCAL.rowDelta > 0) {
queryAddRow(ARGUMENTS.query, LOCAL.rowDelta);
}
// add list data to each cell in the new column
for (LOCAL.i = 1; LOCAL.i <= LOCAL.dataCount; LOCAL.i++) {
querySetCell(ARGUMENTS.query, ARGUMENTS.columnName, LOCAL.data[LOCAL.i], LOCAL.i);
}
// returning isn't necessary because the passed query argument is a reference, return whatever suits your needs
return ARGUMENTS.query;
}
</cfscript>
Assuming the two lists have the same length, you can do something like this.
myQuery = QueryNew(dummy, varchar);
QueryAddColumn(myQuery, Column1, varchar, ListToArray(List1);
QueryAddColumn(myQuery, Column2, varchar, ListToArray(List2);
QueryDeleteColumn(myQuery, dummy);
Related
I'm trying to add the serialized data in a request to third party API which needs a specific order of the data to be maintained, but SerializeJSON orders in alphabetical order which breaks the format required by the third party API. Could someone help me to figure it out
INPUT:
<cfset data ={
"Booking": {
"ActionCode":"DI",
"AgencyNumber":"23",
"Touroperator":"TVR",
"BookingNumber":"323",
},
"Payment": {
"__type":"paymenttype",
"PaymentProfile": {
"Value": 4,
"Manual": false
},
"PaymentType": 4,
"PaymentAction":2,
"Details": {
"IBAN": "DE02120300000000202051",
"BIC": "BYLADEM1001"
}
},
"Login":{
"UserCode": "usercode",
"Password": "password"
}
}>
When this method SerializeJSON() is used on my data:
SerializeJSON(data)
Current Output
"{"Booking":{"Touroperator":"TVR","ActionCode":"DI","BookingNumber":"323","AgencyNumber":"23"},"Login":{"UserCode":"usercode","Password":"password"},"Payment":{"PaymentProfile":{"Manual":false,"Value":4},"PaymentType":4,"PaymentAction":2,"__type":"paymenttype","Details":{"BIC":"BYLADEM1001","IBAN":"DE02120300000000202051"}}}"
Expected Output:
"{"Booking":{"ActionCode":"DI","AgencyNumber":"23","Touroperator":"TVR","BookingNumber":"323",},"Payment":{"__type":"paymenttype","PaymentProfile":{"Value":4,"Manual":false},"PaymentType":4,"PaymentAction":2,"Details":{"IBAN":"DE02120300000000202051","BIC":"BYLADEM1001"}},"Login":{"UserCode":"usercode","Password":"password"}}"
Structs in ColdFusion are unordered HashMaps, so there is no order at all. You can keep insertion order by using structNew("Ordered") (introduced with ColdFusion 2016). Unfortunately you can no longer use the literal syntax anymore, but I assume you are generating the data dynamically anyway.
<cfset data = structNew("Ordered")>
<cfset data["Booking"] = structNew("Ordered")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">
<cfset data["Payment"] = structNew("Ordered")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = structNew("Ordered")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>
etc.
If you are stuck on an older ColdFusion version, you will have to use Java's LinkedHashMap.
<cfset data = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Booking"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Booking"]["ActionCode"] = "DI">
<cfset data["Booking"]["AgencyNumber"] = "TVR">
<cfset data["Booking"]["BookingNumber"] = "323">
<cfset data["Payment"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["__type"] = "paymenttype">
<cfset data["Payment"]["PaymentProfile"] = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Payment"]["PaymentProfile"]["Value"] = 4>
<cfset data["Payment"]["PaymentProfile"]["Manual"] = false>
etc.
But be aware: LinkedHashMap is case-sensitive (and also type-sensitive: in case your keys are numbers, it does matter!).
<cfset data = createObject("java", "java.util.LinkedHashMap")>
<cfset data["Test"] = "">
<!---
accessing data["Test"] = works
accessing data["test"] = doesn't work
accessing data.Test = doesn't work
--->
Another issue you might encounter: Due to ColdFusion's internal type casting, serializeJSON() might stringify numbers and booleans in an unintended way. Something like:
<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = true>
<cfset data["myInteger"] = 123>
could easily end up like:
{
"myBoolean": "YES",
"myInteger": 123.0
}
(Note: The above literal syntax would work perefectly fine, but if you are passing the values around as variables/arguments, casting eventually happens.)
The easiest workaround is explicitly casting the value before serializing:
<cfset data = structNew("Ordered")>
<cfset data["myBoolean"] = javaCast("boolean", true)>
<cfset data["myInteger"] = javaCast("int", 123)>
If I have a query result like this
type_name | count
-----------------
test1 | 5
test2 | 4
test4 | 7
How to output the count base on the value of the column type_name?
From the table the row value 'test3' doesn't exist, but it doesn't mean it wont exist later after a refresh.
With the below code I will only get the value but looping 3 times since test3 value doesn't exist.
<cfoutput name="query">
<table>
<tr><td><cfif query.type_name eq 'test1'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test2'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test3'>#query.count#</cfif></td></tr>
<tr><td><cfif query.type_name eq 'test4'>#query.count#</cfif></td></tr>
</cfoutput>
<cfset testResults = [0,0,0,0]>
<cfloop query="query">
<cfset testResults[right(query.type_name, 1)] = query.count>
</cfloop>
<table>
<cfloop array="#testResults#" index="count">
<td>#count#</td>
</cfloop>
</table>
You should see
5
4
0
7
If you need a more dynamic approach, you can transform your full query result (all columns) to an array of structs:
<!---
<!--- test data --->
<cfset query = queryNew("type_name,count", "VARCHAR,INTEGER")>
<cfset queryAddRow(query, 3)>
<cfset querySetCell(query, "type_name", "test1", 1)>
<cfset querySetCell(query, "count", "5", 1)>
<cfset querySetCell(query, "type_name", "test2", 2)>
<cfset querySetCell(query, "count", "4", 2)>
<cfset querySetCell(query, "type_name", "test4", 3)>
<cfset querySetCell(query, "count", "7", 3)>
--->
<!--- number of tests to list --->
<cfset expectedNumberOfRows = 4>
<!--- remember all columns while transforming query to array of structs --->
<cfset queryColumns = listToArray(query.columnList)>
<!--- initialize array of structs --->
<cfset testResults = arrayNew(1)>
<cfloop from="1" to="#expectedNumberOfRows#" index="i">
<cfset blankResult = structNew()>
<!--- initialize all columns for each row --->
<cfloop array="#queryColumns#" index="columnName">
<cfset blankResult[columnName] = "">
</cfloop>
<!--- default values for specific columns --->
<cfset blankResult["type_name"] = "test#i#">
<cfset blankResult["count"] = "0">
<cfset testResults.add(blankResult)>
</cfloop>
<!--- transfer cell values from each query row to array of structs --->
<cfset queryRowIndex = 1>
<cfloop query="query">
<!--- extract possible row index --->
<cfset testNumber = trim( reReplace(query.type_name, "[^0-9]*([0-9]+)$", "\1") )>
<!--- transfer cells --->
<cfif reFind("^[0-9]+$", testNumber)>
<cfloop array="#queryColumns#" index="columnName">
<cfset testResults[int(testNumber)][columnName] = query[columnName][queryRowIndex]>
</cfloop>
</cfif>
<cfset queryRowIndex++>
</cfloop>
<cfoutput>
<table>
<cfloop array="#testResults#" index="testResult">
<tr>
<td>#testResult.type_name#</td>
<td>#testResult.count#</td>
<!--- add additional columns if desired --->
</tr>
</cfloop>
</table>
</cfoutput>
I'm building a new application using Coldfusion as the business logic layer or controller in an MVC pattern and wanting to setup a binary search algorithm to handle username lookup on a registration function. Are there any documented instances of implementing a binary search algorithm in Coldfusion/sql?
Code Below. Note this is far from perfect.
Just a rough attempt at trying to translate the syntax into CFML
<cffunction name="binarySearchFunc" access="public" returntype="boolean">
<!--- Define array collection variable and target --->
<cfargument name="collection" type="array">
<cfargument name="target">
<!--- If the target is empty, return false --->
<cfif not isDefined(#argument.target#) or is Null>
<cfset returnValue = "false">
<cfreturn returnValue >
<!--- Else set the high, mid and low params for binary search --->
<cfelse>
<cfparam name="low" type="integer" default="0">
<cfparam name="high" type="integer" default="#ArrayLen(collection)#-1" >
<cfparam name="ix" type="integer" default="">
<cfscript>
While( low LTE high){
<!--- set the mid variable equal to the mean value between the low and
high --->
ix = (#low# + #high#)/2
<!--- set a variable equal to compareTo method of the target below --->
intVar = #target#.compareTo(collection[xi] <!--- equivalent CF syntax
??? --->
if (intVar LTE 0 ) {
//target value less than collection[i]
high = ix - 1;
}
else if (intVar GT 0 ){
low = ix ++ 1 ; }
else {
var returnValue = "true";
return returnValue;
}
}
</cfscript>
</cfif>
</cffunction>
I have been tasked with shortening a sequence of numbers by replacing internal sequences of 3 or more with a dash for the internal numbers (as in, 3,5,6,7,8,10 would be 3,5-8,10)
Pretty similar to the above, just using lists.
<cfscript>
a = listToArray('3,5,6,7,8,10,11,15,16,17,18');
writeOutput( fNumSeries(a) );
string function fNumSeries( array a) output="false" {
var tempList = '';
var outputList = '';
for ( var i in a ) {
if( listLen(tempList) == 0 ) {
// Nothing in the list
tempList = i;
} else if ( listLen(tempList) ) {
if ( listLast(tempList)+1 == i) {
// i is next in order from the previous list item. Just add to temp list.
tempList = listAppend(tempList, i);
} else if (listLen(tempList) >= 3) {
// current item is not the next item, and the tempList is more than 2 items long, so abbreviate and set templist to the current item.
outputList = listAppend(outputList, listFirst(tempList) & '-' & listLast(tempList));
tempList = i;
} else {
// tempList is less than 3 items long, just append to output and set tempList to the current item.
outputList = listAppend(outputList, tempList);
tempList = i;
}
}
}
// Clean up the remaining item(s) in tempList. If 3 or more, abbreviate, else just append to output.
outputList = listLen(tempList) >= 3 ? listAppend(outputList, listFirst(tempList) & '-' & listLast(tempList)) : listAppend(outputList, templist);
return outputList;
}
</cfscript>
I put the following function together, plus some demo output. It assumes the numbers are in an ordered, non-repeating array. (CF list functions help get to this point.) Is there a more elegant way of doing this?
<cffunction name="fNumSeries" output="1" hint="parses array of numbers for display: 1|2|3|6|7|9|10|11|12 would be 1-3, 6, 7, 9-12 ">
<cfargument name="raNums" required="1" type="array">
<cfset raLen=arraylen(raNums)>
<cfif raLen eq 0><CFRETURN "">
<cfelseif raLen eq 1><CFRETURN raNums[1]>
<cfelseif raLen eq 2><CFRETURN raNums[1] & "," & raNums[2]>
</cfif>
<cfset numSeries="">
<cfset numHold=raNums[1]>
<cfset seqHold=raNums[1]>
<cfset numSeries=raNums[1]><!--- raNums[1]always output the first number --->
<cfloop from="1" to="#raLen#" index="idxItem">
<cfif idxItem gt 1><!--- wait for 2nd element to decide output --->
<cfif numHold+1 neq raNums[idxItem]><!--- reason to output and seqHold+1 neq numHold--->
<cfif seqHold eq numHold>
<cfset numSeries=numSeries & ",#raNums[idxItem]#">
<cfelse>
<cfif seqHold+1 eq numHold><cfset numSeries=numSeries & ","><!--- and numHold+1 neq raNums[1] and seqHold+1 eq numHold --->
<cfelse><cfset numSeries=numSeries & "-">
</cfif>
<cfset numSeries=numSeries & "#numHold#,#raNums[idxItem]#">
</cfif>
<cfset seqHold=raNums[idxItem]>
</cfif>
</cfif>
<cfset numHold=raNums[idxItem]>
</cfloop>
<cfif seqHold neq numHold><!--- display the last element --->
<cfif seqHold+1 eq numHold or raLen eq 2><cfset numSeries=numSeries & ",">
<cfelse><cfset numSeries=numSeries & "-">
</cfif>
<cfset numSeries=numSeries & "#numHold#">
</cfif>
<CFRETURN numSeries>
</cffunction>
<cfset raRa=[
[2,12],
[2,3,12],
[2,4,12],
[2,3,4,12],
[2,8,9,11,12],
[2,9,11,12],
[2,3,8,10,11,12]
]>
<cfoutput>
<cfloop array="#raRa#" index="ra">
#arrayToList(ra)#<br />
#fNumSeries(ra)#<hr>
</cfloop>
</cfoutput>
I would like to handle a row from a query by a function, where I pass the row as a structure.
ideally...
<cfloop query="myquery">
#myfunction(#row#)#
</cfloop>
I could set it up like this too...
<cfloop query="myquery">
#myfunction(#col1#,#col2#,#col3#,#col4#)#
</cfloop>
but I don't want to. I haven't been able to finda simple way of extracting a row, But I thought I'd ask.
found a little more elegant looking solution, for single row
<cfscript>
function GetQueryRow(query, rowNumber) {
var i = 0;
var rowData = StructNew();
var cols = ListToArray(query.columnList);
for (i = 1; i lte ArrayLen(cols); i = i + 1) {
rowData[cols[i]] = query[cols[i]][rowNumber];
}
return rowData;
}
</cfscript>
Adobe ColdFusion 11 introduced QueryGetRow which converts a row from a query into a struct.
Ben Nadel has posted a blog post about this where he gives an example UDF that converts a query to a struct, and it accepts an optional row argument that allows you to turn an single row in that query to a struct. Take a look here.
This is the Class from Ben's site without the comments
<--- --------------------------------------------------------------------------------------- ----
Blog Entry:
Ask Ben: Converting A Query To A Struct
Author:
Ben Nadel / Kinky Solutions
Link:
http://www.bennadel.com/index.cfm?event=blog.view&id=149
Date Posted:
Jul 19, 2006 at 7:32 AM
---- --------------------------------------------------------------------------------------- --->
<cffunction name="QueryToStruct" access="public" returntype="any" output="false"
hint="Converts an entire query or the given record to a struct. This might return a structure (single record) or an array of structures.">
<cfargument name="Query" type="query" required="true" />
<cfargument name="Row" type="numeric" required="false" default="0" />
<cfscript>
var LOCAL = StructNew();
if (ARGUMENTS.Row){
LOCAL.FromIndex = ARGUMENTS.Row;
LOCAL.ToIndex = ARGUMENTS.Row;
} else {
LOCAL.FromIndex = 1;
LOCAL.ToIndex = ARGUMENTS.Query.RecordCount;
}
LOCAL.Columns = ListToArray( ARGUMENTS.Query.ColumnList );
LOCAL.ColumnCount = ArrayLen( LOCAL.Columns );
LOCAL.DataArray = ArrayNew( 1 );
for (LOCAL.RowIndex = LOCAL.FromIndex ; LOCAL.RowIndex LTE LOCAL.ToIndex ; LOCAL.RowIndex = (LOCAL.RowIndex + 1)){
ArrayAppend( LOCAL.DataArray, StructNew() );
LOCAL.DataArrayIndex = ArrayLen( LOCAL.DataArray );
for (LOCAL.ColumnIndex = 1 ; LOCAL.ColumnIndex LTE LOCAL.ColumnCount ; LOCAL.ColumnIndex = (LOCAL.ColumnIndex + 1)){
LOCAL.ColumnName = LOCAL.Columns[ LOCAL.ColumnIndex ];
LOCAL.DataArray[ LOCAL.DataArrayIndex ][ LOCAL.ColumnName ] = ARGUMENTS.Query[ LOCAL.ColumnName ][ LOCAL.RowIndex ];
}
}
if (ARGUMENTS.Row){
return( LOCAL.DataArray[ 1 ] );
} else {
return( LOCAL.DataArray );
}
</cfscript>
</cffunction>
usage...
<!--- Convert the entire query to an array of structures. --->
<cfset arrGirls = QueryToStruct( qGirls ) />
<!--- Convert the second record to a structure. --->
<cfset objGirl = QueryToStruct( qGirls, 2 ) />
Another solution would be:
function QueryToStruct(query){
var cols = ListToArray(query.columnList);
var salida = query.map(function(v=0,i,a){
return {'#cols[1]#':v};
});
return ValueArray(salida,'#cols[1]#');
}