Basically i've created a search page on my website and I need to display each band name/track/location A-Z. I've successfully listed every band in alphabetical order, however i want one half of the page to have 0-9, A-L on it and the other to have M-Z. Is there a way of using Ascii checks + count to check the first letter of each band and call them to the correct alphabet group? Thanks.
It sounds like you want to split up a query into sets based on the first letter of a string. In CF 10 or Railo 4, you could use Underscore.cfc's groupBy function to accomplish this like so:
_ = new Underscore();
bands_by_name = _.groupBy(bands, function(band) {
var first_letter = left(band.name, 1);
if (isNumeric(first_letter))
return '0-9';
else if (first_letter <= 'L')
return 'A-L';
else
return 'M-Z';
});
groupBy(collection, callback) iterates through a collection, applying the callback function to each item and using the return value to group the collection items into a new struct.
In this case, the bands_by_name struct would have the keys 0-9, A-L, and M-Z, each containing an array of structs containing the relevant results. Example:
{
'0-9': [{name: '1 Band', track: 5, location: 'CA'}, {name: '2Cool', track: 1, location: 'NM'}],
'A-L': [{name: 'Good Band', track: 2, location: 'NY'}],
'M-Z': [{name: 'Some Band', track: 3, location: 'PA'}, {name: 'What a Band', track: 2, location: 'NV'}]
}
Note: I wrote the Underscore.cfc library.
It's a little verbose but it allows for a quick change of columns by adding/changing ascii values to the list bandGroups. It assumes each column will have data.
<!--- syntax may very depending on database --->
<cfquery ...>
Select
bandName
, trackName
, location
, upper(left(bandName,1)) as firstLetter
from
someTable
order by
bandName
</cfquery>
<!---
create a list of ascii values used to break up your columns
this will allow you to easily change the number of columns
End first group at L , end next group at [ (first char after "Z")
--->
<cfset bandGroups = asc("M") & ',' & asc("[")>
<cfset thisGroup = 1>
<cfset header = '<div style = "float: left; width: #int(100/listLen(bandGroups))#%"><table>'>
<cfoutput>#header#</cfoutput>
<cfoutput query = "query" group = "firstLetter">
<tr>
<td colspan="3">
#query.firstLetter#
</td>
</tr>
<cfoutput>
<tr>
<td>#query.bandName#</td>
<td>#query.trackName#</td>
<td>#query.location#</td>
</tr>
</cfoutput>
<cfif query.recordCount eq query.currentRow or asc(query.firstLetter) gte listGetAt(bandGroups, thisGroup)>
<cfset thisGroup++>
</table>
</div>
<cfif query.currentRow neq query.recordCount>
#header#
</cfif>
</cfif>
</cfoutput>
Related
My CF application provide three selections (semicolon, comma or tab) for users to choose to match the delimiters they have in their file. I want to validate what users selected with what delimiter they have in their file. Is there a way to do this?
So if user is using tab delimiters for his text file but he accidentally selected a comma then I will get this error:
Invalid list index 2.
In function ListGetAt(list, index [, delimiters]), the value of index, 2, is not a valid as the first argument (this list has 1 elements). Valid indexes are in the range 1 through the number of elements in the list.
I think the only way to avoid this type of error is if I can validate user's delimiters being used in their file but I could not find any example when I searched the web.
You didn't specify what kind of data is delimited in the file, so here's just a very simple guessing method:
<!--- read file into memory --->
<cfset fileContent = fileRead( expandPath("yourfile.csv") )>
<!--- declare delimiting characters to check, NOTE: due to using "listLen" you may only specify single characters --->
<cfset possibleDelimiters = [ ";", ",", chr(9) ]> <!--- chr(9) = tab --->
<!--- count number of records found for each delimiter --->
<cfset countResults = {}>
<cfloop array="#possibleDelimiters#" index="delimiter">
<cfset countResults[delimiter] = listLen(fileContent, delimiter)>
</cfloop>
<!--- determine delimiter with the highest count --->
<cfset sortedDelimiters = structSort(countResults, "NUMERIC", "DESC")>
<cfset mostFrequentDelimiter = sortedDelimiters[1]>
<cfoutput>
Is <code>#encodeForHtml(mostFrequentDelimiter)# (#asc(mostFrequentDelimiter)#)</code> the delimiter?
</cfoutput>
However, this will guess terribly if you have text paragraphs in your file due to the frequency of commas in most written languages, so take it with a grain of salt.
I have a query as below which
<cfquery name="qryGetXXX" datasource="#sDataSource#">
SELECT COUNT(*) AS TotalCount,Quality.Qualitydesc
FROM QualityCheck INNER JOIN Quality
ON QualityCheck.QualityID = Quality.qualityID
WHERE DATEDIFF(d,DueDate,GETDATE()) >= 90
GROUP BY quality.qualityDesc
</cfquery>
will result in
1) *total count* 21 *QualityDesc* IO
2) *total count* 1 *QualityDesc* Max
3) *total count* 1 *QualityDesc* Min
4) *total count* 1 *QualityDesc* Other
5) *total count* 3 *QualityDesc* Reg
In order to get the first row I am using,
<cfif #qryGetXXX.RecordCount# gt 0 >
<cfloop query="qryGetXXX" startrow="1" endrow="1">
<cfset XXXTimeTotal =#qryGetXXX.TotalCount# >
</cfloop>
<cfelse>
<cfset XXTotal = 0 >
</cfif>
which is checking for the first the recordcount of the whole query but is there any way I can check whether the first row (i.e. startrow 1 and endrow 1) is has a value and then if the startrow 2 and endrow 2 has a value and so on? Can I put the results in an array and will that be easier?
Frank,
You are correct, i am new to coldfusion, hence all the confusion.The query provides me with the results i.e i have the result set as explained above, however i cannot figure out a way to check every row individually. For example i have to check if there are results for the first row and if it does i have to pass that value to my output, and if doesnot i have to put in no value was entered or '0'. I need to do this for the five rows and there will always only be 5 rows of results, which is why i was using start and endrow, however start and endrow donot allow me the flexibility to check for empty row values. In essence i would like to check the row as something like below.
<!--- check the first row of resuslt i.e. Max values ---!>
<cfif startrow1.endrow1 gt 0 >
<cfset nIOCount =#qryGetXXX.TotalCount# >
<cfelse>
<cfset nIOCount = '0'>
</cfif>
<!--- then check the second row resuslts Max ---!>
<cfif startrow2.endrow2 gt 0 >
<cfset nMaxCount =#qryGetXXX.TotalCount# >
<cfelse>
<cfset nMaxCount = '0'>
</cfif>
<!---Output the values---!>
Totals
<tr>
<th>IO</th>
<td>#nIoCount#</td>
</tr>
<tr>
<th>Max </th>
<td>#nMaxCount#</td>
</tr>
<tr>
I know i cannot reference startrow and endrow as i want, so i am looking for a way to reference each row individually in the result set some other way. Any suggestions?
thanks
That query/code is rough (I rendered it to something I could work with).
Your query: (if yours works just leave it as is (I'm making two assumptions below)).
<cfquery name="test" datasource="#sdatasource#">
select count(a.*) as totalcount, a.qualitydesc
from quality a, qualitycheck b
where a.qualityid = b.qualityid
and datediff(d,a.duedate,getdate()) >= 90
group by a.qualitydesc
</cfquery>
Then do your check if and sets like this: (instead of an array I did a struct (making more assumptions as well)).
<cfset totals = structnew()>
<cfif test.recordcount>
<cfoutput query="test">
<cfif test.totalcount neq "">
<cfset StructInsert( totals, test.QualityDesc, test.totalcount )>
<cfelse>
<cfset xxtotal = 0>
</cfif>
</cfoutput>
</cfif>
In fact, you can skip that nested if statement and for the xxtotal and do something else out of the loop leaving you with tighter code that looks like this:
<cfset totals = structnew()>
<cfif test.recordcount>
<cfoutput query="test">
<cfset StructInsert( totals, test.QualityDesc, test.totalcount )>
</cfoutput>
</cfif>
So if these are your targets:
1) totalcount 21 QualityDesc IO
2) totalcount 1 QualityDesc Max
3) totalcount 1 QualityDesc Min
4) totalcount 1 QualityDesc Other
5) totalcount 3 QualityDesc Reg
Then your looped values will look like this and any missing values or whatever will be bypassed (again you will need to do some checking down stream)...
totals.IO = 21
totals.Max = 1
totals.Min = 1
totals.Other = 1
totals.Reg = 3
Let me know if this helps and makes sense.
(Summary from comments...)
If you mean some of the descriptions, like "IO", are not included in query results at all, then that is what I suspected earlier. Since you are using an INNER join, the query will only return counts for descriptions that exist in both tables. If you want to include all descriptions, even if there is no match in "QualityCheck", you need to use an OUTER join instead. For example:
SELECT Quality.Qualitydesc
, COUNT(QualityCheck.QualityID) AS TotalCount
FROM Quality LEFT JOIN QualityCheck
ON QualityCheck.QualityID = Quality.qualityID
AND DATEDIFF(d,DueDate,GETDATE()) >= 90
GROUP BY quality.qualityDesc
Though as I mentioned on another thread, how you construct the filter can negatively impact the query's performance. See What makes a SQL statement sargable? for details and alternatives.
Side note, while you provided a lot of detail here, you omitted the most important part: .. the actual goal ;-) You will get faster and more accurate responses if you clearly summarize what you are trying to do first, then include the code.
The Golden Rule: Imagine You're Trying To Answer The Question
The XY Problem
How to Ask
I'm getting cfquery result like below.This result set for one field value(single line).
Memberof =
"CN=AG-880-ExpenseReports,CN=Users,DC=alcco,DC=com, CN=HTTP Users,CN=Users,DC=alcco,DC=com, CN=WA Houses,CN=Users,DC=alcco,DC=com, CN=MTViewMeadows,CN=Users,DC=alcco,DC=com, CN=ALC0169,CN=Users,DC=alcco,DC=com, CN=ALC0069,CN=Users,DC=alcco,DC=com"
have to take as a list of number ALC0169,ALC0069 from this also i need values only 0169,0069.. from that result set.
Is there a way of doing this with coldfusion?
Here is a very straightforward string processing script that will print out the list of numbers you are looking for based on your description. Where I am printing out the numbers, you will want to capture those into an array or other structure depending on what you want to do with the data.
<cfscript>
memberOf = "CN=AG-880-ExpenseReports,CN=Users,DC=alcco,DC=com,CN=HTTP Users,CN=Users,DC=alcco,DC=com,CN=WA Houses,CN=Users,DC=alcco,DC=com,CN=MTViewMeadows,CN=Users,DC=alcco,DC=com,CN=ALC0169,CN=Users,DC=alcco,DC=com,CN=ALC0069,CN=Users,DC=alcco,DC=com";
memberOf = Replace(memberOf, "CN=Users,DC=alcco,DC=com", "", "all");
memberOf = Replace(memberOf, ",,", ",", "all");
memberOf = Replace(memberOf, "CN=", "", "all");
memberArray = ListToArray(memberOf);
</cfscript>
<cfoutput>
#memberOf#<br/><br/>
<cfloop array="#memberArray#" index="i">
<cfif Left(i, 3) eq "ALC">
#Right(i, Len(i)-3)#<br/>
</cfif>
</cfloop>
</cfoutput>
I would use list functions. Note that you can specify your own delimiter. From your other post, your cfldap tag returned a query named GroupSearch.
<cfset ALCNumbers = "">
<cfloop list = "GroupSearch.MemberOf" Index = "MemberOfThis">
<cfif ListFirst(MemberOfThis, delimiters = "=") is "CN"
and left(ListLast MemberOfThis, delimiters = "="), 3) is "ALC">
<cfset ALCNumbers = ListAppend(ALCNUmbers, mid(ListLast MemberOfThis, delimiters = "="), 4,
len(ListLast MemberOfThis, delimiters = "=") - 3)>
</cfif>
</cfloop>
This may have syntax errors because I simply typed it into the textarea. However, it shows the general idea.
I'm in Coldfusion 8. I have a table that is produced by a loop. Very complex code but I put some of it here:
<cfloop array = #qrep.getColumnList()# index = "col">
<cfset l = l + 1>
<cfif l EQ tindx - 1>
<cfset prevcol= col>
</cfif>
<cfif linefold GT 0>
<cfset lmod = i%linefold>
<cfelse>
<cfset lmod = 1>
</cfif>
<!--- printing detail --->
<cfif l LE m AND repdetail NEQ 'n'>
<td class = "repsubthead"> Subtotal:
<b>#qrep[col][currentrow]#</b></td>
</cfif>
<!--- printing totals only; row labels --->
<cfif repdetail EQ 'n' AND l EQ tindx >
<cfset frowarr[footrow] = qrep[col][currentrow]>
<cfset footrow_1 = footrow - 1>
<cfif footrow EQ 1>
<td style = "font-size: 13px" > #qrep[col][currentrow]#</td>
<cfelseif frowarr[footrow] NEQ frowarr[footrow_1] >
<td style = "font-size: 13px;"> #qrep[col]currentrow]#</td>
<cfelse>
<cfset testrow = footrow>
<td class = "repsubthead" style = "padding-top: 10px"> Total #qrep[prevcol] currentrow]# </td>
</cfif>
.... lots more before we get to end of loop
This part of the code prints out a row label for each row. Further in the program there is a similar loop to print out the value for the row. Everything is working fine except for one problem I can't trace. An extra row is being inserted in one spot, with no data in it. Part of the table is here:
State: CT
AVS 25.00
COMB 15.00
Email2010 15.00
REF 75.00
STRLST01 22.00
extra row inserted here, height much smaller than other rows
STRLST04 50.00
Total CT 202.00
I have copied this table to a Libre Office document and zoomed in on the bad row. It is definitely there, and it contains a blinking item that looks like this: '
I cannot delete this item from the row in Libre Office, although I am able to delete the entire row. The blinking thing disappears when I put my cursor in another row.
I have checked both STRLST01 and STRLST04 in my MySQL database, and they seem fine, with no anomalies. I cannot find anywhere in my code where I would be inserting an extra row (altho admittedly the code is very complicated).
Has anyone seen something like this? Does anyone have a clue what might be causing this?
Just a shot in the dark here... but try sanitizing your table content. For example:
#htmlEditFormat(qrep[col][currentrow])#
This would be to rule out that the TR in "STRLST01" isn't getting processed as <TR>. I've seen dynamic tables like this one go haywire because the content gets interpreted as HTML.
MC
What is the generated HTML? Looking at the actual generated output rather than the rendered output could help you find if it's bad data being dumped into another row or if it's a bug in your HTML generation routine.
If it's an odd special character in your data, the HTMLEditFormat() or XMLFormat() functions should find it and deal with it. Or at least make it easier to troubleshoot.
It's a bit of tricky question, however, my page for most rated bands displays the band logos in order of how high they have been rated. My only problem is i want to count through the records by using a cfloop from 1 to 10 and because it is split in to two columns, have one counting 1 through to 9 and the other 2 through to 10, each of them with steps of two.
Can anybody help me with this? If i've confused just mention it and ill try to clarify exactly what i mean.
<DIV class="community_middle">
<cfoutput query="top10MostRated">
<cfloop from="2" to="10" index="i" step="2">
<DIV class="communityContent">
#chr(i)#
<IMG src="logo/#top10MostRated.Logo#" alt="#top10MostRated.Name#" width="100%" height="100%"></IMG>
</DIV>
<BR/>
</cfloop>
</cfoutput>
</DIV>
If you're looking to do odd/even lists separately, then you can use the currentrow property of the query combined with the modulo operator (%) to work out if the row is odd or even:
<cfloop query="topBands>
<cfif topBands.currentRow % 2 = 1>
<!--- do your odd number output here --->
</cfif>
</cfloop>
<cfloop query="topBands>
<cfif topBands.currentRow % 2 = 0>
<!--- do your even number output here --->
</cfif>
</cfloop>
I think these answers address your side-by-side part of your question but does not explain the "same image" issue. Their code is written correctly but does not explain the reason.
Your code:
<IMG src="logo/#top10MostRated.Logo#"
alt="#top10MostRated.Name#"
width="100%" height="100%"></IMG>
... would be fine if you were only inside a <cfloop query = "top10MostRated"> or <cfoutput query = "top10MostRated"> block. The reason is because inside these types of blocks CF is smart enough to know you want the data for the current row. It would be the same as:
<IMG src="logo/#top10MostRated.Logo[top10MostRated.currentRow]#"
alt="#top10MostRated.Name[top10MostRated.currentRow]#"
width="100%" height="100%" />
Because you're nesting the to/from cfloop inside a <cfoutput query = ""> block, you are getting unexpected results. Your existing code is always asking for the record provided by your outer loop. Hence you see the same image 5 times. (using any of the fine examples provided will help you get out of this) but, you can remove the query from your cfoutput and simply ask CF to show you the value for the correct row in your loop using your index (you set your index to "i") so the below would show you the image that corresponds to your loop.
<IMG src="logo/#top10MostRated.Logo[i]#"
alt="#top10MostRated.Name[i]#"
width="100%" height="100%" />
It sounds like what you'd like to get is a collection of even-numbered records and a collection of odd-numbered records. In Coldfusion 10 or Railo 4, you can use groupBy() from Underscore.cfc to split up your query result into manageable sub-sets, like so:
_ = new Underscore();// instantiate the library
groupedBands = _.groupBy(topBands, function (val, index) {
return index % 2 ? "odd" : "even";
});
This returns a struct with two elements odd and even, each containing an array of records which are odd or even. Example result:
{
odd: [{name: "Band one"}, {name: "Band three"}],
even: [{name: "Band two"}, {name: "Band four"}]
}
Splitting your results into logical sub-sets makes the code more readable:
<cfoutput>
<cfloop from="1" to="5" index="i">
<div class="left">#groupedBands.odd[i].name#</div>
<div class="right">#groupedBands.even[i].name#</div>
</cfloop>
</cfoutput>
You'll also be able to use those sub-sets in other places on your page if you need to.
Note: I wrote Underscore.cfc
Ben Nadel has a post exactly for this. Link here
A breakdown for this is
<cfloop query="top10MostRated">
<cfif top10MostRated.CurrentRow MOD 2>
<!--- Add to the "odd list" --->
<cfelse>
<!--- Add the record to the "even list" --->
</cfif>
</cfloop>
Then you'll have 2 lists oddList and evenList. Then it's just a matter of displaying them.
I'd do it a different way. The objective is to have records 1 and 2 side by side and I don't see that in #barnyr's answer.
<cfoutput>
<cfloop from="2" to="topbands.recordcount + 1" index = "i" step="2">
#topbands.fieldname[i-1]#
<cfif i lte topbands.recordcount>
#topbands.fieldname[i]# <br />
</cfif>
</cfloop>
</cfoutput>