And On The Seventh Row, MOD Created 1, And It Was Good
On Ray's Friday Puzzler, it came up that not everyone knows what the MOD (or Modulus) operator is in math. This is one of those things that once you know it, you can't imagine how you lived with out it. The MOD operator does a fairly simple thing: it divides two numbers and returns only the remainder.
To demonstrate how this works in a practical sense, let's run this code:
<!--- Loop over value range. ---> <cfloop index="intValue" from="1" to="15" step="1"> <!--- Get the result of this Value MOD'ed by 3. Remember, this will divide 3 into the value and return the remainder. ---> #intValue# MOD 3 = #(intValue MOD 3)#<br /> </cfloop>
Running the above code, we get:
1 MOD 3 = 1
2 MOD 3 = 2
3 MOD 3 = 0
4 MOD 3 = 1
5 MOD 3 = 2
6 MOD 3 = 0
7 MOD 3 = 1
8 MOD 3 = 2
9 MOD 3 = 0
10 MOD 3 = 1
11 MOD 3 = 2
12 MOD 3 = 0
13 MOD 3 = 1
14 MOD 3 = 2
15 MOD 3 = 0
Notice that we can see a sequence coming through: 1,2,0. The sequence is as long as the number we are using to MOD. This is because every Nth value should be evenly divisible by (N) and then the remainder gets "reset."
This can be a headache saver for ColdFusion query output when you want to format alternating rows. To figure out which row you are on (odd vs. even), all you have to do is MOD the current row by 2. If 2 divides evenly into the row number (and thereby resulting in a remainder of zero), we are on an even row. If we MOD by 2 and have a remainder, we must be on an odd row.
<!--- Loop over query. ---> <cfloop query="qData"> Row #qData.CurrentRow# is: <!--- Check to see if this row is even or odd. If this row number MOD 2 has a reminader (evaluates to TRUE in ColdFusion), then we are in an odd row. ---> <cfif (qData.CurrentRow MOD 2)> ODD <cfelse> EVEN </cfif> </cfloop>
Running the above, we get:
Row 1 is: ODD
Row 2 is: EVEN
Row 3 is: ODD
Row 4 is: EVEN
Row 5 is: ODD
Row 6 is: EVEN
Tons of stuff has been written about the Modulus operator. It's totally awesome. When working on Ray's puzzler, I was having a huge mental block trying to figure out how to get rid of the zero in my sequence. Here is a great resource I found that talks about using and manipulating sequences using the modulus operator.
Go forth and MOD.
Want to use code from this post? Check out the license.
Btw, I never got to comment on your issues with being a senior programmer. I am not sure what that means given my being self employed or working for interweb startups and small dev companies for the last 10 years. But I have been reading your blog for the last 4-5 months and I can truthfully say that you are a damn good programmer. You have taught me quite a bit.
Another handy use: creating a specific number of columns. I use this all the time since I find myself creating "sets" of checkboxes. If I have 11 checkboxes it's easier to read if they're laid out so that there are 2 rows of 4 and one row of 3.
To do that, I usually give the parent element a pre-defined width and float it to the left. Each successive checkbox then is added to a single row. I can then just test the current index MOD 4 (current index % 4 in php and others) and, if the value is 1, set the element class to "startrow" or some such. To that class I apply clear: left to force the creation of a new row.
As you say...handy.
Thanks for the kind words. Glad that I can help out.
Yeah, excellent use case. Along those same lines, outputting over a calendar can be quite helpful (MOD 7).
learned this trick a LOOOONG time ago:
Instead of using MOD to find out if a number is ODD or EVEN, use BITAND(). An example:
BitAnd(8,1) would return 0 which means it's EVEN
BitAnd(5,1) woudl return 1 which means it's ODD
Using the MOD function, CF must internally perform a lot of calculations in order to see if there is a remainder or not. BitAnd just uses BitWise logic to see if the number's first bit is a 0 or 1.
I doubt you will see a performance difference on small sites, but if your pumping out 10,000 could make a difference.
Using mod is much better than the:
[cfif color is "white"]
[cfset color = "gray"]
[cfset color = "white"]
[tr bgcolor = "#color#"]
that I've seen in places...
BTW, I loved the headline too
Tony - great idea!
That is very slick! Bit manipulation is one of those dark arts that I think very few people (including myself) really understand how to take advantage of. I have used Bits as a way to group "flags" for objects, but nothing like this.
Thanks for the awesome tip. And yes, I can see this would be a much better performance feature (as division never actually needs to take place). Cool!
thanks for props guys.
But like most of the cool ideas in my head, they all have an originating source, though most of the time I don't remember where :P
Is a discussion on the CF Cookbook about this topic:
You might not have invented it, but you sure get credit for pointing it out to me:
There is a faster way to do Even/Odd checks
int OMask = 1;
if( (someotherint & OMask) == OMask)
There is no bit-wise AND operator in ColdFusion. Instead, you would have to use the BitAnd() method:
<cfif BitAnd( qData.CurrentRow, 1 )>
... ODD ...
... EVEN ...
As far as this method being faster, I suppose it would be because it's not actually performing any mathematical dvision (as would be required for MOD). Really, we are just taking the binary representation of the number and examining the first digit. I assume that this would be a constant speed no matter how large the number. Thanks for the good tip.
I just wrote something similar before stumbling upong this post. I had a hard time deciphering MOD so I made this script so I could see how it works visually.
<cfloop from="1" to="10" index="i">
<cfloop from="1" to="10" index="z">
<cfset res = z MOD i />
<li>#z# MOD #i# = #res#</li>
I was trying to get it to execute on the third iteration. What I found is doing x MOD y = 0, where y is your desired iteration (in my case 3) seems to work in any case. Run the code and see for yourself!
I am not sure what you mean?
I'm referring to using MOD in a cfoutput query, where you'd need every other row highlighting, or a line break after every third item, or fifth. I've found the easiest and most legible way is to do :
currentRow MOD 2 EQ 0
currentRow MOD 3 EQ 0
or currentRow MOD 5 EQ 0 respectively.
The only need I've had for MOD thus far has been in a query loop, so I guess it's a narrow perspective.
Ahh, gotcha. Yeah, I think that's how most people need to use MOD. The only time I've had to use it otherwise is for sequential number outputs of some sort.