4

firstly, I very much apologize for my poorly written title of this question. So please someone with native English, change the title appropriately. My question is rather simple, it follows:

I am using integer to store multiple types of one item. For example:

TYPE A = 1
TYPE B = 2
TYPE C = 4
TYPE D = 8
TYPE E = 16
etc...

Now the item in DB has type value 14, that means that it has been assigned to TYPE B+C+D. If it would have type value for example 9, it would mean it has been assigned to TYPE A+D.

I need a function which I would supply with single type integer and this function would return array of integer types.

I could iterate through all the integers and compare them with the number, but that's what I am using now, but I am looking for some more effective way, if it exists ?

Thanks in advance for your help.

Frodik
  • 14,986
  • 23
  • 90
  • 141
  • It sounds like you're trying to use a [bitmask](http://en.wikipedia.org/wiki/Mask_(computing)); typically you "retrieve" values via binary AND operations. – user229044 Nov 08 '11 at 13:18

4 Answers4

1

How's this? http://codepad.org/AzgdPsL1

To explain what's going on:

  1. I create a $types array of all the valid types that exist between the range of 1 to $max_bit bits.
  2. Looping while the number is greater than 0, it gets bitwise ANDed with 1. If it turns out that evaluates to true, this means that the LSB is set, so the type at the head of the $type array applies to this number. The current type is added to the return array.
  3. The number is then shifted to the right by one bit.

    <?php
    
    function num2type( $num)
    {
        $max_bit = 5;
        $types = array_combine( range( 1, $max_bit), range( ord( 'A'), ord( 'A') + $max_bit - 1));
    
        $return = array();
        while( $num > 0)
        {
            $current_type = array_shift( $types);
            if( $num & 0x1)
            {
                $return[] = chr( $current_type);
            }
            $num = $num >> 1;
        }
        return $return;
    }
    
    var_dump( num2type( 8)); // array(1) { [0]=> string(1) "D" }
    var_dump( num2type( 31)); 
    var_dump( num2type( 14));
    

Output (for 31):

array(5) {
  [0]=>
  string(1) "A"
  [1]=>
  string(1) "B"
  [2]=>
  string(1) "C"
  [3]=>
  string(1) "D"
  [4]=>
  string(1) "E"
}

Output for 14:

array(3) {
  [0]=>
  string(1) "B"
  [1]=>
  string(1) "C"
  [2]=>
  string(1) "D"
}
nickb
  • 59,313
  • 13
  • 108
  • 143
1
$a = 10;
$scan = 1;
$result = array();
while ($a >= $scan){
    if ($a & $scan)
        $result[] = $scan;
        $scan<<=1; //just a bit shift
}
var_dump($result);
J0HN
  • 26,063
  • 5
  • 54
  • 85
1
function check_flag($field, $bit)
{
    return (($field | $bit) === $field) ? TRUE : FALSE;
}

$types = array('A' => 1, 'B' => 2, 'C' => 4, 'D' => 8, 'E' => 16);
$db_value = 14;
var_dump(check_flag($db_value, $types['B']));

... just make sure that you cast the value fetched from the database to integer.

Edit: Now that I read you need all of the types that are set, here's some more logic:

$set = array();
foreach ($types as $key => $value)
    if (check_flag($db_value, $value)) $set[] = $key;
Narf
  • 14,600
  • 3
  • 37
  • 66
1

Here's a function without any loops (mostly done for the fun of it :) ):

function getTypes($int)
{
    $types = array('Type A','Type B','Type C','Type D','Type E');//defining types
    $t = array_reverse(str_split(decbin($int)));//converting $int to an array of bits
    $types = array_slice($types,0,ceil(log($int,2)));//slicing the array to the same size as number of bits in the $int
    $t = array_combine($types,$t);// Creating a new array where types are keys and bits are values
    return array_keys($t,1);// returning an array of keys which have a value of 1
}

However it doesn't mean that it is efficient. If you use a bitmask you better of checking values using bitwise operators like bitwise and (&). For example if you want to check if your integer contains Type D and Type E you should do

if ($integer & 8 & 16)

To check for each individual type I would us a loop with bitshift operator

function getTypes($int)
{
    $result = array();
    $types = array('Type A','Type B','Type C','Type D','Type E');
    foreach($types as $type)
    {
        if ($int & 1)//checking if last bit is 1 (exactly the same as if($int%2) )
            $result[]=$type;
        $int>>=1;//shifting integer's bits to the right (exactly the same as $int = $int / 2)
    }
    return $result;
}
Ivan
  • 3,567
  • 17
  • 25
  • 1
    Maybe I'm not getting something, but it doesn't work at all. Throws 2 warnings and returns null. – J0HN Nov 08 '11 at 13:44
  • My bad, I forgot to slice types array to the correct size. Should work fine now. – Ivan Nov 08 '11 at 13:49