How to get dynamic property names? - coldfusion

I've got array of key names and a structure containing the data.
I need to loop through the structure and print out all the property values, based on the key-array.
my key value contains for example values
propList array contains:
id, name, haircolor, age
I want to go through the structure and print out values for
item.id
item.name
item.haircolor
item.age
(or any properties stored in the array)
I've tried to print out
<cfoutput>"#item.propName#"</cfoutput>
where propName is retrieved from looping through propList array.

Do you have an array of structures or something? if you only have one level of properties in the structure you don't need the two loops? You just have to do:
<cfloop array="#myStructures#" item="item">
<cfloop array="#propList#" index="key">
<cfoutput>#item[key]#</cfoutput>
</cfloop>
</cfloop>
Or to be safer:
<cfloop array="#myStructures#" item="item">
<cfloop array="propList" index="key">
<cfif StructKeyExists(item, key)>
<cfoutput>#item[key]#</cfoutput>
</cfif>
</cfloop>
</cfloop>

You can use array notation to get values form a structure when the key is 'dynamic'.
<cfloop index="item" array=#input#>
<cfloop list="#rawColumnList#" delimiters="," index="value">
<cfoutput>#item[ value ]#</cfoutput>
</cfloop>
</cfloop>

a solution:
<cfloop index="item" array=#input#>
<cfloop list="#rawColumnList#" delimiters="," index="value">
<cfset output=Evaluate("item.#value#")>
<cfoutput>#output#</cfoutput>
</cfloop>
</cfloop>

Related

Coldfusion limit cfloop to 10

I'm working with quite a large array of items in a cfloop. I would like to pare this down and add pagination. Is there anyway in my cfloop to limit the array to the first 10?
I have
<cfloop array="#qryItems#" index="index">
I have tried turning it into a condition loop with no luck and a few other things. I haven't touched coldfusion in a while and am a little rusty. Google is not helping haha
I have tried
<cfloop from="1" to="10" array="#qryItems#" index="index">
and have also tried max_rows
<cfloop maxrows="10" array="#qryItems#" index="index">
each time I get the error message
"Attribute validation error for tag CFLOOP."
<cfloop from="1" to="10" index="index">
<!--- Then do your business with array elements qryItems[index], that is, with qryItems[1], qryItems[2],..., qryItems[10] --->
</cfloop>
There is no combination of attributes for cfloop to accomplish what your expecting. As BKBK suggested, you'll need to use a from/to loop to output a select group of records. If I understand your requirements correctly, I would update your cfloop with a new index variable as well, and then set the old variable by referencing the array element.
The two cfloops below output the same data, with the second displaying only the records in the pagination range.
<cfset qryItems = [1,2,3,4,5,6,7,8,9,10,'a','b','c','d'] />
<cfoutput>
<!--- Current loop: Outputs all records --->
<cfloop array="#qryItems#" index="index">
#index#
</cfloop>
<cfset paginationStart = 1 />
<cfset paginationEnd = 10 />
<!--- Only the range of of records requested --->
<cfloop from="#paginationStart#" to="#paginationEnd#" index="indexNumber">
<cfset index = qryItems[indexNumber] />
<!--- code remain the same --->
#index#
</cfloop>
</cfoutput>

Need to insert the Upload count in the database and in table

I am getting the following details in my form. I need to loop through the fields which have "attachment" in the name and add the count to the table, and also the values in the new table.
ATTACHMENT C:\ColdFusion10\cfusion\runtime\work\Catalina\localhost\tmp\neotmp3230094756217875313.tmp
ATTACHMENT2 C:\ColdFusion10\cfusion\runtime\work\Catalina\localhost\tmp\neotmp4341408903737742616.tmp
ATTACHMENT3 C:\ColdFusion10\cfusion\runtime\work\Catalina\localhost\tmp\neotmp2809169853442728277.tmp
I am trying to do a loop over the form fields, but I am lost in between, and am unsure where to proceed.
<CFLOOP collection="#structform#" item="whichField">
<cfif FindNoCase('attachment',whichField)>
<cfset total = len(whichField)>
<cfoutput>#total#</cfoutput><br><br>
</cfif>
<CFOUTPUT>#whichField# = #structform[whichField]#</CFOUTPUT><br>
</CFLOOP>
I'm still not certain I've understood what's going on, so this might need tweaking/changing to make it work as needed, but this is without doubt a better approach:
<cfset Total = 0 />
<cfloop collection=#StructForm# item="FieldName" >
<cfif findNoCase('attachment',FieldName) AND len(StructForm[FieldName]) >
<cfset ++Total />
</cfif>
</cfloop>
<CFLOOP collection="#structform#" item="whichField">
<cfif FindNoCase('attachment',whichField)>
<cfset lstvalue = ListAppend(lstvalue,whichField)>
</cfif>
</CFLOOP>
<cfset total = ListLen(lstvalue)>
<cfif (len(total) AND total EQ 1) AND (Evaluate(lstvalue) EQ '')>
<cfset total = 0>
</cfif>

returning a struct of queries

If a series of queries are created like so:
<cfloop list="#platform_list#" index="x">
<cfquery name="#trim(x)#" dbtype="query">
<!--- stuff to build the query --->
</cfquery>
</cfloop>
and I then return the queries in a struct like so:
<cfset queries_RET = StructNew() />
<cfloop list="#platform_list#" index="x">
<cfif StructKeyExists(args, #trim(x)#)>
<!--- here's where I think things go horribly wrong --->
<cfset queries_RET[x] = #x# />
</cfif>
</cfloop>
<cfreturn queries_RET />
And then when returned to the calling function as "graphData", I try to access it like so:
<cfloop list="#platform_list#" index="x">
<cfif StructKeyExists(url, x) and StructKeyExists(graphData, x)>
<cfloop query="graphData[x]">
I get an error at the last line:
Attribute validation error for tag cfloop.
The value of the attribute query, which is currently graphData[x], is invalid.
The value of the struct at graphData[x] is a string having the same value as the element name... so what do I do to assign the query with that name instead? I'm sure it's ridiculously obvious. :(
EDIT:
I'll give the answer to Shawn, although I finally figured out what my underlying issue(s) are. First, I didn't realize that
<cfset queries_RET[x] = #x# />
doesn't actually assign the query to the element at x, but instead a reference to it. To assign the actual query object, I need to do this:
<cfset queries_RET[x] = #Evaluate(x)# />
Second, when the struct is returned to the calling function, calling
<cfloop list="#Application.platform_list#" index="x">
<cfloop query="#graphData[x]#">
didn't work because the query attribute of cfloop is looking for a reference to a query object --- and the query object it was looking for didn't exist since it hadn't been returned.
Finally, now that I am actually returning a valid query object, that query attribute still doesn't work, because now graphData[x] isn't a reference. To make it work, I have to first assign a reference, and use that as the query attribute in the cfloop:
<cfloop list="#Application.platform_list#" index="x">
<cfset thisQuery = #graphData[x]#>
<cfloop query="thisQuery">
I think that my fundamental problem was not understanding the the query attribute isn't an actual query object, but a reference to one instead. It's been a learning curve!
Try
<cfloop list="#platform_list#" index="x">
<cfif StructKeyExists(url, x) and StructKeyExists(graphData, url[x])>
<cfset q = graphData[x]>
<cfloop query="q">
If this throws
The value of the attribute query, which is currently q, is invalid
Then you should cfdump the q make sure it is a query object.
The <CFLOOP> query attribute takes the name of a query (string), not an actual query object.
When you pass something into that attribute, CF expects it to be a string.
If it's a literal, CF will expect it to be the name of a valid query.
If it's a variable, CF will expect the value of the variable to be equal to the name of the query.
Therefore, the output you ultimately want is:
<cfloop list="#platform_list#" index="x">
<cfloop query="#x#">
Here is a full snippet of code that confirms this, which you may refer to/compare to for your own code:
<cfset query_a = QueryNew('id,name,pass') />
<cfset QueryAddRow(query_a)>
<cfset QuerySetCell(query_a,'id',1)>
<cfset QuerySetCell(query_a,'name','joe')>
<cfset QuerySetCell(query_a,'pass','joe123')>
<cfset query_b = QueryNew('id,name,pass') />
<cfset QueryAddRow(query_b)>
<cfset QuerySetCell(query_b,'id',4)>
<cfset QuerySetCell(query_b,'name','pete')>
<cfset QuerySetCell(query_b,'pass','pete123')>
<cfset query_c = QueryNew('id,name,pass') />
<cfset QueryAddRow(query_c)>
<cfset QuerySetCell(query_c,'id',7)>
<cfset QuerySetCell(query_c,'name','frank')>
<cfset QuerySetCell(query_c,'pass','frank123')>
<cfset platform_list = 'query_a,query_b,query_c' />
<cfloop list="#platform_list#" index="x">
<cfloop query="#x#">
<cfoutput>#id# #name# #pass#</cfoutput><br/>
</cfloop>
</cfloop>
You'll notice in this snippet that if you change the query attribute back to "x" (rather than #x#) you'll produce the exact same error you are encountering now.
Why?
Answer: platform_list is a comma-delimited list of strings (which x becomes via the loop), not a comma-delimited list of Query objects.

How to Break out of Cfoutput

I am looping through the results of a query, and I need to limit the number of rows displayed. I need to use cfoutput because I'm using the group attribute, and I cannot use the maxrows because not all rows will be displayed.
I tried to use <cfbreak> inside the <cfoutput>, but that throws an error.
How can I break out of the <cfoutput> loop?
If your group by is only there to remove duplicates from your results I would suggest using your query to cut them down then you can cfloop (select distinct and reducing your returned column list).
If you are using your group by to "group" your results You could run a counter within your loop and a cfif statement inside your first loop to omit later results.
You could fake the group by option in your cfloop by matching value from previous row if you need cfbreak
<cfloop query="queryname">
<cfif queryname.column[currentrow-1] neq queryname.column[currentrow]>
#queryname.column#
</cfif>
</cfloop>
Random note: you can maxrows on any/all levels of your grouped cfoutput
<cfset tmp = querynew('id,dd')>
<cfloop from="1" to="20" index="i">
<cfset queryaddrow(tmp,1)>
<cfset querysetcell(tmp,'id',rand(),i)>
<cfset querysetcell(tmp,'dd',(i mod 4),i)>
</cfloop>
<cfquery dbtype="query" name="tmp">select * from tmp order by dd</cfquery>
<cfoutput query="tmp" group="dd" maxrows="2">#dd#<br
<ul>
<cfoutput maxrows="2" group="id"><li>#id#</li></cfoutput>
</ul>
</cfoutput>
You could use the cfthrow tag to trigger an exception that will allow you to break out of the loop using cfcatch you can then ignore the exception and continue processing. That will give you what you want.
<cftry>
<cfset i = 0>
<cfoutput query="qMyQuery" group="someGroup">
<cfset i = i + 1>
Parent
<cfoutput>
Child
</cfoutput>
<cfif i GTE 10>
<cfthrow type="break">
</cfif>
</cfoutput>
<cfcatch type="break">
<!--- DO NOTHING - THIS IS A HACK FOR NOT BEING ABLE TO USE CFBREAK inside cfoutput. --->
</cfcatch>
</cftry>

Evaluate List of ColdFusion Variables

i created a table using coldfusion lists system, rows are months and the columns are cities, and the variable is the sales, now i need to add to every evaluated sale variable, the list of companies that made these sales, so far there is no problem with writting the sql of this table and the sales, but i cant define-#evaluate# the list of these companies that belongs to each sale in the table, since it is the LIST of companies, not one variable like sale, i hope i made myself clear :) here is my code:
The query with some sets:
<cfset month_list_name = "#message13#,#message2#,#message3#,#message4#,#message5#,#message6#,#message7#,#message8#,#message9#,#message10#,#message11#,#message12#">
<cfset type_index = 'NETTOTAL'>
<cfquery name="GET_CITY" datasource="#DSN#">
SELECT CITY_ID,CITY_NAME FROM SETUP_CITY ORDER BY CITY_NAME
</cfquery>
<cfquery name="GET_COMPANY" datasource="#DSN#">
SELECT COMPANY_ID,FULLNAME FROM COMPANY ORDER BY COMPANY_ID
</cfquery>
<cfset c_index = 'COMPANY_ID'>
<cfset no_cities = 0 />
<cfset cities_list = ''>
<cfset cities_list_name = ''>
<cfset month_list = ''>
<cfoutput query="get_city"><cfset cities_list = listappend(cities_list,city_id)><cfset no_cities ++ /></cfoutput>
<cfoutput query="get_city"><cfset cities_list_name = listappend(cities_list,city_name)></cfoutput>
<cfloop from="1" to="12" index="i"><cfset month_list=listappend(month_list,i)></cfloop>
<cfquery name="GET_SALES_TOTAL" datasource="#dsn#">
SELECT
SUM(NETTOTAL) NETTOTAL,
SC.CITY_ID,
DATEPART(MM,INVOICE_DATE) AY,
C.FULLNAME,
C.COMPANY_ID AS COMPANY_ID
FROM
#DSN2_ALIAS#.INVOICE I,
SETUP_CITY SC,
COMPANY C
WHERE
MONTH(INVOICE_DATE) >= 1
AND MONTH(INVOICE_DATE) < 12
AND C.COMPANY_ID=I.COMPANY_ID
AND SC.CITY_ID=C.CITY
AND PURCHASE_SALES=1
GROUP BY
DATEPART(MM,INVOICE_DATE),
SC.CITY_ID,
C.FULLNAME,
C.COMPANY_ID
</cfquery>
here is the definitions for lists:
<cfloop list="#month_list#" index="month_index">
<cfloop list="#cities_list#" index="city_index">
<cfoutput query="GET_SALES_TOTAL">
<cfif city_id eq city_index and AY eq month_index>
<cfloop list="#type_index#" index="tt_index">
<cfset 'alan_#tt_index#_#city_index#_#month_index#' = evaluate(tt_index)>
<cfset 'alan2_#city_index#_#tt_index#_#month_index#' = evaluate(tt_index)>
</cfloop>
<cfloop list="#c_index#" index="cc_index">
<cfif cc_index eq company_id>
<cfset 'company_#cc_index#_#city_index#_#month_index#' = evaluate(cc_index)>
</cfif>
</cfloop>
</cfif>
</cfoutput>
</cfloop>
</cfloop>
and here's the table:
<cfloop list="#month_list#" index="m_index">
<tr class="color-row" height="20">
<td width="150" nowrap><b><cfoutput>#left(listgetat(month_list_name,listfind(month_list,m_index)),20)#</cfoutput></b></td>
<cfloop list="#cities_list#" index="ddd_other">
<cfloop list="#type_index#" index="ii_index">
<td align="center">
<cfif isdefined('alan_#ii_index#_#ddd_other#_#m_index#') and len(evaluate('alan_#ii_index#_#ddd_other#_#m_index#'))>
<cfset all_toplam=evaluate('alan_#ii_index#_#ddd_other#_#m_index#')+all_toplam>
<cfset 'total_#ii_index#_#m_index#'=evaluate('total_#ii_index#_#m_index#') + #evaluate('alan_#ii_index#_#ddd_other#_#m_index#')#>
<cfset 'total2_#ddd_other#'=evaluate('total2_#ddd_other#') + #evaluate('alan2_#ddd_other#_#ii_index#_#m_index#')#>
<cfif listfindnocase('NETTOTAL',ii_index)>
<cfif isdefined('attributes.money') and attributes.money is 2>
<cfoutput>#TLFormat((evaluate('alan_#ii_index#_#ddd_other#_#m_index#')/get_money.rate2),2)#</cfoutput>
<cfelse>
<cfoutput>#TLFormat(evaluate('alan_#ii_index#_#ddd_other#_#m_index#'),2)#</cfoutput>
</cfif>
<cfelse>
<cfif isdefined('attributes.money') and attributes.money is 2>
<cfoutput>#TLFormat((evaluate('alan_#ii_index#_#ddd_other#_#m_index#')/get_money.rate2),2)#</cfoutput>
<cfelse>
<cfoutput>#TLFormat(evaluate('alan_#ii_index#_#ddd_other#_#m_index#'),2)#</cfoutput>
</cfif>
</cfif>
<cfelse>
0
</cfif>
<cfloop list="#c_index#" index="co_index">
<cfif listfindnocase('COMPANY_ID',co_index)>
<cfoutput>#evaluate('company_#co_index#_#ddd_other#_#m_index#')#</cfoutput>
</cfif>
</cfloop>
</td>
</cfloop>
</cfloop>
<cfloop list="#type_index#" index="kk_ind">
<td align="center" class="txtbold">
<cfif isdefined('attributes.money') and attributes.money is 2>
<cfoutput>#TLFormat((evaluate('total_#kk_ind#_#m_index#')/get_money.rate2),2)#</cfoutput>
<cfelse>
<cfoutput>#TLFormat(evaluate('total_#kk_ind#_#m_index#'),2)#</cfoutput>
</cfif>
</td>
</cfloop>
</tr>
</cfloop>
it says: Variable company_COMPANY_ID_1_1 is undefined. where is my mistake?
i cant evaluate the LIST of company_idies in the same way i evaluated the NETTOTAL, since net total is not the list, but only one value... And thank you all for the help and attention! ;)
There are a lot of things you can improve in this code.
Incrementing the number of cities inside a loop is unnecessary. You can get the count directly from SQL (select count(*) from setup_city) or from CF after building your list (listlen(cities_list).
For that matter, it's unnecessary to loop through the list manually when you can simply use ValueList to have CF do the work for you:
cities_list = ValueList( get_city.city_id );
cities_list_name = ValueList( get_city.city_name );
There's no reason to build a list of months if all you're ever going to do is loop from 1 to 12. Use the 1-to-12 loop instead.
type_index doesn't need to be a list if you only have one item. Use the variable instead. Replace it with a list only when you actually need a list; this will also remove the need to use evaluate, which is making your code hard to read and difficult for people to follow. Same thing for cc_index.
Because scopes can be accessed as if they were structs, it's generally better (and can be significantly faster) to use structkeyexists( attributes, "money" ) rather than isdefined( "attributes.money" ). Specifying the scope for isdefined does help, of course ... isdefined( "money" ) is not only slow, but could return a completely different variable if you're not careful.
Speaking of structs, I would definitely recommend that you use them rather than concatenated variable names. It's extremely difficult to parse what you have written in order to understand what you are trying to do: using structs would make it easier to follow.
Finally, it looks to me like you're doing a ton of work in ColdFusion to do something that your database could do for you much more easily. If you need grand totals for companies by city, why not just do this?
<cfquery name="GET_SALES_GRAND_TOTAL" datasource="#dsn#">
SELECT
SUM(NETTOTAL) NETTOTAL,
SC.CITY_ID,
C.FULLNAME,
C.COMPANY_ID AS COMPANY_ID
FROM
#DSN2_ALIAS#.INVOICE I,
SETUP_CITY SC,
COMPANY C
WHERE
MONTH(INVOICE_DATE) >= 1
AND MONTH(INVOICE_DATE) < 12
AND C.COMPANY_ID=I.COMPANY_ID
AND SC.CITY_ID=C.CITY
AND PURCHASE_SALES=1
GROUP BY
SC.CITY_ID,
C.FULLNAME,
C.COMPANY_ID
</cfquery>
Is there a need to check that the month is between 1 and 12? If INVOICE_DATE exists, then it has to have a month between 1 and 12. I would either remove those lines from the query entirely or replace them with INVOICE_DATE IS NOT NULL.
You should then be able to get your table of results with something that looks roughly like this:
<cfoutput query="GET_SALES_TOTAL" group="AY">
<!--- months first --->
<tr>
<!--- month-specific stuff goes here --->
<td>month_header</td>
<cfoutput group="city_id">
<!--- city-specific stuff --->
<td><!--- format NETTOTAL however you want here ---></td>
</cfoutput>
</tr>
</cfoutput>
<tr>
<td>grand_total_header</td>
<cfoutput query="GET_SALES_GRAND_TOTAL" group="city_id">
<td><!--- format NETTOTAL here ---></td>
</cfoutput>
</tr>
Hopefully this is of some help to you. I would strongly encourage you to read up on arrays and structs; someone using evaluate is typically doing it the hard way when there is a much easier way of managing things, and there are plenty of examples both on SO and elsewhere of CF code that you should be able to learn from.