View Full Version : Most efficient way of having a list of items to choose from

06-27-2007, 07:05 PM
What is the easiest way to have a list of items that people can choose from, and have that data stored in a database?

Right now I'm using checkboxes and powers of 2....

value of option 1 is 2
value of option 2 is 4
value of option 3 is 8

But if I have 5 options I would have 31 different possibilites, and I was wondering if there was an easier way to do this then just running if statements for every possible value.

Is there a way I could use a loop to do this?

06-27-2007, 07:56 PM
You want to use bitwise operators, which you're on the right track with the powers of 2 thing. I might be able to explain it...

Let's say your menus have the following permission values:

itemA: 1
itemB: 2
itemC: 4
itemD: 8
itemE: 16

And a user has chosen items B, C, and E to see, so his flag value is 22 (2 + 4 + 16).

Now let's look at the binary values:

itemA: 00001
itemB: 00010
itemC: 00100
itemD: 01000
itemE: 10000

user flag value: 10110

You can determine access for each particular menu item by performing a bitwise "ADD" operation (a single "&").

if ((int)$itemA & (int)$userFlag == $itemA) {
echo "menu itemA activated";
if ((int)$itemB & (int)$userFlag == $itemB) {
echo "menu itemB activated";
if ((int)$itemC & (int)$userFlag == $itemC) {
echo "menu itemC activated";
if ((int)$itemD & (int)$userFlag == $itemD) {
echo "menu itemD activated";
if ((int)$itemE & (int)$userFlag == $itemE) {
echo "menu itemE activated";

We can easily put that into a loop with an array, but for now let's keep them separate. What that "&" bitwise operator is doing is comparing the bits, one at a time, and spitting out a result of 1 if both bits are 1, and 0 if one or two of the bits are 0. Compare each bit in the example below:

00001 ($itemA)
10110 ($userFlag)
00000 ($result)

If you look at each bit separately, there aren't any bits that are both 1-- they are either "1 and 0" or "0 and 0", so the result is a row of zeros.

And another example:

00010 ($itemB)
10110 ($userFlag)
00010 ($result)

This time the bit 2nd from the right (the "2" bit) both have 1's in them, so the result shows a 1 in the "2" bit, and the result is 00010, or an integer of 2. When we bitwise "add" 00010 and 10110, the result is 00010, and we can use that fact to compare $itemB to $result and if they are equal then that means that bit is ON.

I probably did not explain that well... but it works great. Here's the previous code using an array and a loop:

$menuArray = array(array("itemA", 1),
array("itemB", 2),
array("itemC", 4),
array("itemD", 8),
array("itemE", 16));
foreach($menuArray as $menuVal) {
if ((int)$menuVal[1] & (int)$userFlag == $menuVal[1]) {
echo $menuVal[0]." is activated!";

06-27-2007, 09:16 PM
thanks, that really helps..

The only problem is I can't figure out how to make it work with my checkboxes in the form... =/

06-27-2007, 09:39 PM
Oh... I thought you had that part done already...

value of option 1 is 2
value of option 2 is 4
value of option 3 is 8

That's the ticket, then just add the checkbox values up and save the sum to the user's database info.

06-27-2007, 09:45 PM
I have that, but isn't there something else I have to do to make the code work with my form, because, of course, just running it as the action of my form won't work.

06-27-2007, 10:11 PM
I'm not following your train of thought. Your form gets submitted and it runs a php script which processes the checkboxes that got checked

06-27-2007, 10:12 PM
yes that's true, but how do I implement the code you provided into that?

06-27-2007, 10:21 PM
I'd advise against using a bitflag for a menu. What if you have more than 32 items? Things get complicated after that.

... and do these bitwise operations the right way:


$items = array(
'item A',
'item B',
'item C',
'item D',
'another item',
'yet another item'

$flag = bindec(strrev('100110'));

foreach($items as $bit => $item)
if($flag & (1 << $bit))
print 'item "' . $item . '" enabled' . "\n";

06-27-2007, 10:33 PM
Maybe I'm missing something here - isn't Crazydog saying he just needs things listed that are selected to be stored in a database? I got lost on the bitwise operator solution - and the powers of 2. It doesn't matter (shouldn't matter) what is displayed in the lists or their values, the selection(s) will get into the DB regardless...right? Or am I really out in left field?

06-27-2007, 11:10 PM
Marek_mar that is a great way to do it as well, but you'd then be storing a string that looks like a binary value in your database, right? So rather than store an integer = 22, you'd store a char = "10110". Is that just so it's readable at a glance which bits are on and which are off?

Yeah so I'd use Marek_mar's code and yes you get into trouble when the items count goes over 32.

06-27-2007, 11:42 PM
I appreciate all these suggestions, but I still can't figure out how I am supposed to stick them into my form processor...that's the trouble I'm having now.

06-28-2007, 12:05 AM
If you use Marek's code then you'll want to store the checkbox values as a binary string like this: '111111' Each '1' represents a checked checkbox. So in your script, build that string and stick it in your table.

ralph l mayo
06-28-2007, 03:12 AM
Storing binary as a string defeats the whole minor performance/memory advantage that would lead you to use a bitfield in the first place. Just store rational values and use an enum type to store them very efficiently, or use sequential integers that join on a table that keys integer => rational value for nearly as much efficiency and great normalization justice.

06-28-2007, 08:36 AM
If you use Marek's code then you'll want to store the checkbox values as a binary string like this: '111111' Each '1' represents a checked checkbox. So in your script, build that string and stick it in your table.
The flag is not a string. In the example $flag is int(25).