I have an array:
<cfset fullarray = listtoArray (listofcollumnvalues)>
And it had for example:
fullarray[1]=20
fullarray[2]=11
fullarray[3]=4
fullarray[4]=12.2
etc.
And I wanted to add the sum of the the X position below number:
for example if I wanted the sum of the second element and below
2 + 3 + 4= 27.2
ColdFusion 10 introduced the ArraySlice function. It returns an array when you give it an array, a starting location, and an optional length. So ArraySlice(myArray,3,4) would return a "sub-array" with the elements that start at position 3 and includes 4 elements.
Based on your example:
mySum = ArraySum(ArraySlice(fullarray,2))
If you're on CF 9 or below, you can use a UDF. At CFLib.org there is
arraySlice
arraySlice2
Warning! arraySlice uses "start" and "end" element arguments, while arraySlice2 uses "start" and "length" arguments, like the built-in CF10 function.
Here's something that will work on older versions.
<cfscript>
function listSum(listStr)
{
var delim = ",";
if(ArrayLen(Arguments) GTE 2)
delim = Arguments[2];
return ArraySum(ListToArray(listStr, delim));
}
</cfscript>
Simply envoke it as #listSum(MyListOrArray)#
This is a formatted comment. You define your array as this:
<cfset fullarray = listtoArray (listofcollumnvalues)>
The variable, listofcollumnvalues looks like it might represent results of a query object. If that's the case, You might be doing more work than necessary, because each column of a query is effectively an array. Consider for example this query:
<cfquery name = "queryName">
select field1, numericalField
from etc
</cfquery>
You can actually do stuff like this:
numericalFieldSum = ArraySum(queryName['numericalField'];
It's not exactly what you want, but it illustrates that you might not need your initial command.
Continuing with the assumption that you started with a query, a query of queries might give you what you want.
<cfquery name = "mySum" dbtype = "query">
select sum(numericalField) theSumIWant
from queryName
where your conditions are met
</cfquery>
Of course, if listofcollumnvalues does not come from a query, you should declare this answer to be useless twaddle and downvote accordingly.
Related
I created sample code using the Microsoft SQL Server Northwind demo database. If you don't have access to this demo database here is a simple (MS-SQL) script to create the table and a row of data for this question.
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nvarchar](40) NOT NULL,
[SupplierID] [int] NULL,
[CategoryID] [int] NULL,
[QuantityPerUnit] [nvarchar](20) NULL,
[UnitPrice] [money] NULL CONSTRAINT [DF_Products_UnitPrice] DEFAULT (0),
[UnitsInStock] [smallint] NULL CONSTRAINT [DF_Products_UnitsInStock] DEFAULT (0),
[UnitsOnOrder] [smallint] NULL CONSTRAINT [DF_Products_UnitsOnOrder] DEFAULT (0),
[ReorderLevel] [smallint] NULL CONSTRAINT [DF_Products_ReorderLevel] DEFAULT (0),
[Discontinued] [bit] NOT NULL CONSTRAINT [DF_Products_Discontinued] DEFAULT (0),
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[ProductID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Products] ON
GO
INSERT [dbo].[Products] ([ProductID], [ProductName], [SupplierID], [CategoryID], [QuantityPerUnit], [UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued]) VALUES (1, N'Chai', 1, 1, N'10 boxes x 20 bags', 18.0000, 39, 0, 10, 0)
GO
SET IDENTITY_INSERT [dbo].[Products] OFF
GO
Here is the ColdFusion code:
<cfset variables.useTempVar = false>
<cfquery datasource="Northwind2014" name="qryNWProducts">
SELECT TOP 1 * from Products;
</cfquery>
<cfdump var="#qryNWProducts#" label="qryNWProducts">
<cfset variables['stProduct'] = {}>
<cfloop index="vcColName" list="#qryNWProducts.columnlist#">
<cfif variables.useTempVar>
<cfset variables['temp'] = qryNWProducts[vcColName]>
<cfset variables['stProduct'][vcColName] = variables.temp>
<cfelse>
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName]>
</cfif>
</cfloop>
<cfdump var="#variables['stProduct']#" label="variables['stProduct']">
<cfloop collection="#variables['stProduct']#" item="key"><cfoutput>
variables['stProduct']['#key#'] JVM datatype = #getMetadata(variables['stProduct'][key]).getName()#<br>
</cfoutput></cfloop>
<br>
This always works:<br>
<cfset variables['aPhrase'] = "I ordered " & variables.stProduct.ProductName & " for " & DollarFormat(variables.stProduct.UnitPrice) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
<br>
With "variables.useTempVar = false", the next line will throw a "Complex object types cannot be converted to simple values. " error.<br>
<cfset variables['aPhrase'] = "I ordered " & variables['stProduct']['ProductName'] & " for " & DollarFormat(variables['stProduct']['UnitPrice']) & ".">
<cfoutput>#variables['aPhrase']#<br></cfoutput>
The code above has a boolean variable named "variables.useTempVar" at the top that can be flipped to see the error that I'm getting.
It looks like the direct assignment (when variables.useTempVar = false) from the query to the structure causes the structure values to be of JVM type "coldfusion.sql.QueryColumn".
Another note: if this line of code:
<cfset variables['stProduct'][vcColName] = variables.temp>
is changed to:
<cfset variables['stProduct'][vcColName] = variables['temp']>
The JVM datatype will be "coldfusion.sql.QueryColumn".
When the dot notation temp variable is used to assign the query field (when variables.useTempVar = true); the JVM datatypes are simple types that matches up pretty well with the database column types (java.lang.Integer, java.math.BigDecimal, java.lang.String, etc.).
I've also experiemented with statements like this and that provided some odd results:
<cfset variables['stProduct'][vcColName] = qryNWProducts[vcColName].toString()>
Here's the question. Is this the best way to transfer the simple values from a query to a structure? It seems odd to be forced to use a temp variable and dot notation to make this work.
Comment: I've always thought that dot notation and associative array notation were equivalent. This code example appears to contradict that opinion.
#Leigh is correct in that you need to supply the row number when using associative array notation with a query object. So you'd reference row 1 like: qryNWProducts[vcColName][1]
As for your question
Is this the best way to transfer the simple values from a query to a structure?
Are you sure you need a struct? Your question doesn't really specify the use case, so it is entirely possible that you would be better off using the query object as-is.
If you do need it to be a struct (and since you are using ColdFusion 11) might I suggest you take a look at serializeJSON/deSerializeJSON to convert this to a struct. The serializeJSON has a new attribute that will properly serialize a query object into an "AJAX friendly" JSON array of structs. You can then deSerialize the JSON into a CF array, like so:
NWProducts = deSerializeJSON( serializeJSON( qryNWProducts, 'struct' ) )[1];
Which would return a struct representation of the first row in that query object.
Although it's not obvious from the Adobe docs for serializeJSON, the second parameter can be one of: true|false|struct|row|column which will change how the resulting data is formatted.
Here's a runnable example of using the above technique showcasing each serializeQueryAs option.
It's also a better practice to start moving that kind of code into cfscript. queryExecute is quite easy to use and makes script based queries very easy to develop. See the How To Create a Query in cfscript tutorial at trycf.com for more on how to develop script based queries.
Final note, and this is a bit off topic but it is a generally accepted best practice to not use Hungarian Notation when naming variables.
#Abram's covered the mains answer, but just to pick up one tangential point you raise.
Dot notation and associative array notation are generally equivalent in CFML. However in the case of queries, there is a slight variation. Dot notation: query.columnName is treated as shorthand for query.columnName[currentRow] (where currentRow defaults to 1).
Associative array notation with queries does not have this "syntactic sugar", so query["columnName"] refers to the entire column, as the syntax actually indicates.
There are no functions I am aware of in CFML that take a query column as an argument, however the CFML engine will convert the column to an array if it's used in an array function. This is quite handy sometimes.
I am converting a ColdFusion application to C# (I'm a CF n00b).
I have a script that performs a cfquery and then cfloop's through the results, and it appears to be trying to compare the current row to its following row. And it appears to be trying to make sure that it doesnt try to read past the end of the array.
<cfquery name="qTripLegs" datasource="#sdb#">
SELECT ...
</cfquery>
<cfloop query="qTripLegs">
<cfif (customs_stop[currentrow] NEQ "" OR fuel_stop[currentrow] NEQ "") AND recordcount GT currentrow AND departure[currentrow] NEQ arrival[currentrow+1]>
It feels like currentrow is 1-based (currentrow will have a value of 1 when it first enters the cfloop). Am I correct? I have looked in the coldfusion documentation and I dont see anything about this.
Yes, queries and arrays in CF are 1-based.
The CurrentRow and RecordCount variables are properties of the query (inside a query loop they are automatically scoped).
<cfloop query="QueryName">...</cfloop> will loop through the entire query*, from 1 to QueryName.RecordCount, and the QueryName.CurrentRow index is automatically populated/incremented appropriately. Its value prior to query loop isn't used.
*(unless cfbreak/etc used)
Also to point out there is generally no need to prevent reading past the end (as above, the query loop handles it), it's only because CurrentRow+1 is being used that it's needed to avoid an error.
query.currentRow()
Returns the current row number
queryCurrentRow(query) → returns numeric
Member Function Syntax
<cfscript>
var myQuery = queryNew("id,title","integer,varchar",[[1,"Charlottes Web"],[3,"The Outsiders"],[4,"Mieko and the Fifth Treasure"]]);
cfloop(query = "myQuery"){
if (title Eq "Mieko and the Fifth Treasure"){
writeOutput(myQuery.currentRow());
}
}
</cfscript>
I am trying to write a code that takes a URL that has 3 parts (www).(domainname).(com) and trim the first part out completely.
So far I have this code that checks if on the left side I don't have a 'www' or 'dev'
go in and set siteDomainName = removecharsCGI.SERVER_NAME,1,2);
if (numHostParts eq 3 and listfindnocase('www,dev',left(CGI.SERVER_NAME,3)) eq 0) {
siteDomainName = removecharsCGI.SERVER_NAME,1,2);
The problem with the code above is that is deleting only 2 characters where I need it to delete ALL characters until numHostParts eq 2 or at least until the first "."
Another example would be:
akjnakdn.example.com I need the code to delete the first part of the URL with the dot included (akjnakdn.)
This code will help some of the queries that i have on the site to stop crushing because they are related with the #URL# and when the #URL# is fake I am getting cform query returned zero records error that is causing my contact forms to stop working.
You can just use listRest. It returns all the elements in a list, except the first one. Documentation is here http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-6d87.html
Example:
<cfscript>
name = cgi.server_name;
if (listlen(name,".") gte 3) {
name = listRest(name,".");
}
</cfscript>
You could do something like this:
<cfscript>
local.nameArr = ListToArray(CGI.SERVER_NAME, '.');
if (ArrayLen(local.nameArr) gt 2) {
ArrayDeleteAt(local.nameArr, 1);
}
siteDomainName = ArrayToList(local.nameArr, '.');
</cfscript>
I've split the server name into array elements with a period as the delimiter. If the number of elements is greater than two, remove the first element. Then convert it back to a list with the period as a delimiter.
UPDATE
As suggested by Robb, this could be more concise and perform better by skipping the array conversion process:
<cfscript>
siteDomainName = CGI.SERVER_NAME;
if (ListLen(siteDomainName, '.') gt 2) {
siteDomainName = ListDeleteAt(siteDomainName, 1, '.');
}
</cfscript>
I would use a regular expression, since you only want to "trim" certain subdomains (www,dev).
<cfset the_domain = REReplaceNoCase(cgi.SERVER_NAME, "(www|dev)\.", "") />
Just use a |-delimited list of subdomains you want to trim within the parentheses.
This is my code:
returnStruct.myList = myList;
returnStruct.first = trim(ListGetAt(myList,3));
returnStruct.last = trim(ListGetAt(myList,13));
returnStruct.address = trim(ListGetAt(myList,15));
returnStruct.city = trim(ListGetAt(myList,2));
returnStruct.state = trim(ListGetAt(myList,9));
Everything is working fine until myList hits empty values and then everything crashes.
I found a command "includeEmptyValues" that I can set to 'yes' but I am not familiar with it and the documentation of ColdFusion 9 isn't the best I've come across.
http://cfquickdocs.com/cf9/#listgetat
Previous versions of ColdFusion (and CF9 by default) counted consecutive delimiters as a single delimiter. So a list that looked like this:
<cfset myList="a,b,,c,,d" />
was considered to have four elements.
Recently added is the "includeEmptyValues" attribute.
listGetAt(list, position [, delimiters, includeEmptyValues ])
So while
<cfset myVar=listGetAt(myList,6) />
will throw an error
<cfset myVar=listGetAt(myList,6,",","true") />
will successfully set myVar to d.
might want to use listToArray(), and ArrayIsDefined(). Play with includeEmptyFields attr and see which behavior you prefer. True = Convert empty elements in a list to empty array entries
How would I use use a string function to select everything in a variable after the last "/"
http://domain.com/g34/abctest.html
So in this case I would like to select "abctest.html"
Running ColdFusion 8.
Any suggestions?
Um, a bit strange to give very similar answer within few days, but ListLast looks as most compact and straightforward approach:
<cfset filename = ListLast("http://domain.com/g34/abctest.html","/") />
And yes, IMO you should start with this page before asking such questions on SO.
Joe I'd use the listToArray function, passing the "/" as the delimiter, get the length of the array and get the value in the last slot. See sample below
<cfset str = "http://domain.com/g34/abctest.html"/>
<cfset arr = ListToArray(str,"/","false")/>
<cfset val = arr[ArrayLen(arr)]/>
<cfoutput>#str# : #val#</cfoutput>
produces
http://domain.com/g34/abctest.html : abctest.html