Friday ColdFusion Contest

This post is more than 2 years old.

It's been a few weeks (months?) since my last Friday ColdFusion Contest, but I had an interesting idea today while on the treadmill. The news reported that the amount of lost luggage had declined in 2009 over 2010. They reported that "11 out of a 1000" people reported lost luggage. That struct me as a bit odd as normally you see a figure in the form of "N out of 100." It then occurred to me that it must just be that the percentage is less than one percent. Most folks probably wouldn't be able to grok 0.11% at 7AM.

Your task today - and remember - this should take no more than five minutes - is to write a UDF that accepts a percentage value and returns a string. For percentages from 1 to 100, it should be return "N out of 100". For percentages less than 1 it should return "N out of 1000", or, if you want, make it go even deeper, for example, converting 0.001 to "1 out of 10000". (And if my math is wrong, I blame the lack of coffee!) You could also handle rounding numbers, so 5.6 becomes "6 out of 100."

As before, I'll be giving away a 20 dollar Amazon Gift Certificate. The "winner" will be 100% arbitrary and flattery will be counted.

Brian Rinaldi informed me that my math is wrong. Therefore, Brian Rinaldi will never win a contest again. Thanks Brian. ;) So 11 out of 1000 is really 1.1%. So that actually adds an interesting twist. Should you convert 1.1 to "1 out of 100" (ie, round down), or "11 out of 1000"? I'm not going to answer that - but will leave it up to you guys to decide how to answer ir.


Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Connor Middleton posted on 3/26/2010 at 5:45 PM

BTW, best pic ever!

Comment 2 by Olivier Bridgeman posted on 3/26/2010 at 5:51 PM

11 out of 1000 is actually 1.1%, but even that is an awkward percentage. So perhaps we should we convert percentages less than 2 to N out of 1000? For example: 1.2% would be 12 out of 1000.

Comment 3 by Raymond Camden posted on 3/26/2010 at 5:53 PM

@Olivier - Yep, I was wrong. Brian Rinaldi pinged me on IM to give me a math beatdown. (Reload the blog post to see new last comment.) I'll take _any_ interesting solution as an entry.

Comment 4 by Kevin Schmidt posted on 3/26/2010 at 6:19 PM

function causePanic( percent )
{
var data = StructNew();
data['statistic'] = RandRange( 0,100 ) & ' out of 100 people become zombies each year!';
data['message'] = 'Run for the hills! The Zombie invasion has begun!';
data['note'] = 'Lies, damned lies and statistics!'

return data;
}

That ought to do!

Comment 5 by Danny Scott posted on 3/26/2010 at 6:23 PM

<cffunction name="outOfConvert" access="public" returntype="string">
<cfargument name="inPercent" type="numeric" required="true">
<cfset var outSTR = "">
<cfset var percent = arguments.inPercent/>
<cfset var outOf = 100/>

<cfloop condition="#percent# LESS THAN 1">
<cfset percent *= 10/>
<cfset outOf *= 10/>
</cfloop>
<cfset percent = round(percent)/>
<cfset outSTR = percent & " out of " & outOf/>
<cfreturn outSTR/>
</cffunction>

The zombie one is way better than mine. And it's an important safety tip!

Comment 6 by Curt Gratz posted on 3/26/2010 at 6:52 PM

OK, Total Cheat, but I combined the two entries above to get you "ZombieStats"

function zombiesStats(normalStat) {

var zombiesStat = "";
var humans = arguments.normalStat;
var Zombies = 100;

while (humans LT 1) {
humans *= 10;
Zombies *= 10;
}

humans = round(humans);
zombiesStat = humans & " OUT OF " & Zombies;

return zombiesStat;
}

Comment 7 by Andy Sandefer posted on 3/26/2010 at 7:29 PM

Nice one @Kevin - LOL - I heart zombies. You've gotta check out that new show on Comedy Central about the zombies and demons - it's pretty good.

Comment 8 by Josh posted on 3/26/2010 at 9:50 PM

Here's my attempt

<cffunction name="percentToString" access="public" returntype="string">
<cfargument name="enteredPercent" type="numeric" required="true">
<cfargument name="TwentyFourHrNewsNetworkStatistics" type="boolean" required="false" default="false">

<cfset var length = 0>
<cfset var percent = 0>
<cfset var multiplier = 1>
<cfset var newnum = 0>

<cfif arguments.TwentyFourHrNewsNetworkStatistics>
<cfreturn RandRange(0,100) & " out of 100">
</cfif>

<cfif arguments.enteredPercent gte 1>
<cfset percent = arguments.enteredPercent / 100>
<cfset length = len(percent) - 2>
<cfelse>
<cfset percent = arguments.enteredPercent>
<cfset length = len(percent) - 1>
</cfif>

<cfloop from="#length#" to="1" step="-1" index="i">
<cfset multiplier = multiplier & 0>
</cfloop>

<cfset newnum = percent * multiplier>

<cfreturn newnum & " out of " & multiplier>

</cffunction>

Comment 9 by Raymond Camden posted on 3/26/2010 at 10:07 PM

So far, only two "real" submissions - chances are good for you guys. ;)

Comment 10 by Michael posted on 3/26/2010 at 10:11 PM

<cffunction name="percentString" returntype="string">
<cfargument name="percentIn" type="numeric">
<cfset var numDecPlaces = Len(ListLast(percentIn, "."))>
<cfset var outOf = 100>
<cfif FindNoCase(".", percentIn) lte 0>
<cfset numDecPlaces = 0>
</cfif>
<cfset outOf = outOf & RepeatString('0', numDecPLaces )>
<cfreturn ReplaceNoCase(percentIn, ".", "", "ALL") & " out of " & outOf>
</cffunction>
<cfoutput>
#percentString(1)#<br />
#percentString(.25)#<br />
#percentString(25)#<br />
#percentString(1.1)#<br />
#percentString(1000)#<br />
#percentString(100)#<br />

</cfoutput>

Comment 11 by Rick Hopper posted on 3/26/2010 at 10:32 PM

I don't have access to ColdFusion right now so I can't test to make sure there are no errors. But here are my thoughts...

<cffunction name="ConvertPercentToString" access="public" returntype="string">
<cfargument name="pct" type="numeric" required="true">

<!--- Create return variable --->
<cfset var s = "">

<!--- Convert percent to decimal --->
<cfset var dec = (pct / 100)>

<!--- Set defaults --->
<cfset var iNumDecimalPlaces = -1>
<cfset var iNumericValuePosition = -1>

<!--- Convert to string and remove leading "0." --->
<cfset dec = ToString(dec)>
<cfset dec = Replace(dec, "0.", "", "ALL")>

<!--- Loop over remaining digits of string so we can get count of decimal places and mark position of first non-zero digit --->
<cfloop from="1" to="#Len(dec)#" index="iNumDecimalPlaces">
<cfif (Int(Mid(dec, iNumDecimalPlaces, 1)) > 0) AND (iNumericValuePosition EQ -1)>
<cfset iNumericValuePosition = iNumDecimalPlaces>
</cfif>
</cfloop>

<!--- If non-zero digit found, then use it and number of decimal places to build string --->
<cfif (iNumericValuePosition GT -1)>
<cfset sNumericPortion = Right(dec, (Len(dec) - iNumericValuePosition))>
<cfset s = sNumericPortion & " out of " & (10 ^ (iNumDecimalPlaces - 1))>
</cfif>

<cfreturn s>

</cffunction>

Comment 12 by David Hammond posted on 3/26/2010 at 10:36 PM

I swear I didn't read Danny or Curt's entries before writing this. It's very similar, but I'm submitting it anyway since it converts 90% to 9 out of 10 instead of 90 out of 100, which seems more like how it would appear in a news report. Also, I have added more flattery, which ought to count for something

<cffunction name="NOutOf" output="false" returntype="string">
<cfargument name="percent" type="numeric" required="true">
<cfset var n = arguments.percent/100>
<cfset var outof = 1>
<cfloop condition="round(n) is not n">
<cfset n = n*10>
<cfset outof = outof*10>
</cfloop>
<cfreturn "#NumberFormat(n)# out of #NumberFormat(outof)# people think that Ray's blog is awesome.">
</cffunction>

Comment 13 by Bob W posted on 3/26/2010 at 10:37 PM

My entry (changed your rules slightly to also allow 90% to be returned as "9 out of 10" - all those dentists can't be wrong!)
=====

<cffunction name="textPercent" access="public" output="false" returntype="any">
<cfargument name="percent" type="any" required="true"/>
<cfset var local = StructNew()>
<cfset local.rc = "Invalid percentage!">
<cfif isvalid('Numeric',arguments.percent)>
<cfset local.p = arguments.percent/10.0>
<cfset local.multiplier=10>
<cfloop index="local.i" from="1" to="4" step="1">
<cfif local.p eq int(local.p)><cfbreak></cfif>
<cfset local.p *= 10>
<cfset local.multiplier *= 10>
</cfloop>
<cfset local.rc = local.p & ' out of ' & local.multiplier>
</cfif>
<cfreturn local.rc />
</cffunction>

Comment 14 by Jon Hartmann posted on 3/26/2010 at 11:29 PM

I've got no access to a ColdFusion box to test this (or even a CFML aware text editor). Hope this works:

<cffunction name="XOutOf" output="false" returntype="string">
<cfargument name="percentage" type="numeric" required="true" />

<cfset var factor = "1#RepeatString("0", Len(ListLast(arguments.percentage, "."))#" />

<cfreturn "#arguments.percentage * factor# out of #10 * factor#" />
</cffunction>

Comment 15 by Raymond Camden posted on 3/26/2010 at 11:31 PM

Just an FYI, going to make a random pick, I mean err a serious, hard choice at 4PM CST.

Comment 16 by Raymond Camden posted on 3/27/2010 at 5:43 AM

Guys - I apologize. I totally dropped the ball on this. Going to pick a winner tomorrow.

Comment 17 by Loony2nz posted on 3/27/2010 at 11:16 AM

"That struct me as a bit odd..." A wee bit too much coldfusion vernacular? hehehe (some english beatdown for an english major? hehe)

Comment 18 by Raymond Camden posted on 3/29/2010 at 9:39 PM

Some results. I used this as a 'test harness':

<cfset tests = "1,2,3,0.1,0.11,0.5,1.5">

<cfset theFunc = x>

<cfloop index="t" list="#tests#">
<cfoutput>#t# = #thefunc(t)#<br/></cfoutput>
</cfloop>

where x was the udf to test.

@Danny - yours didn't seem to work for 0.1 or 0.11.

@Josh: for 0.1 it set 10 out of 100 - it got all the x.y ones wrong.

@Michael: Yours seems right, but, I'll ding it a bit for returning "01 out of 1000" and "011 ..." notice it left a trailing 0.

@Rick - I had to change one > to gt, but it had the same issue as Michael.

@David - ok, flattery aside, so far this is the best, and I like the it used numberformat.

@Bob - yours worked well as well.

@JonH: Ha to add one more )... it returned it wrong off by a factor of 10... but interesting example there!

So being that I'm 3 days late (sorry guys again!) I'm going with David! David, please ping me via email with your preferred email for the Amazon gift cert, and thanks everyone!

Comment 19 by Josh posted on 3/29/2010 at 10:00 PM

Ray, I was taking 0.1 to mean 10%. 10 out of 100 is 10%, although it should have returned 1 out of 10 at that point. Strangely enough, .1 will return 1 out of 10. I assumed anything under 1 was to be treated as the actual percentage, ie .25 = 25% and 5.6 = 5.6%.

Comment 20 by Raymond Camden posted on 3/29/2010 at 10:06 PM

Makes sense - I hope I didn't come off too critical - I was just trying to judge this quickly as I had been so late in it!

Comment 21 by Josh posted on 3/29/2010 at 11:58 PM

No, I just didn't want to have my intent misinterpreted.