简体   繁体   中英

Get array values based on bitmask in PHP

I have a 32-bit integer used for bitmask and an array with 32 values. How to get only those values from the array which indexes correspond to the positions of the non-zero bits in the bitmask?

For example let say that the bitmask is 49152 which is 1100000000000000 in binary. Therefore I have to take the values of elements with indexes 14 and 15 from the array.

You will need to loop in 32 steps over your mask and test it for '1', if this bit is set, you can copy the element to your resulting array.

Pseudo code:

m = 0x00000001
j = 0
for i in 0 to 31 loop
  if ((mask & m) = m) then   // bit is set in mask
    result(j++) := input(i)
  end if
  m := m << 1     // shift left by 1 or multiply by 2
end loop

Here's some PHP code for you, but please note it is pretty inefficient. I have used this algorithm because:

  1. It is explicit and uses words rather than mathematical tricks to get the job done, and
  2. Works in situations where you can't assume an underlying implementation of 2s complement, but still want to 'think' that way. (try 'storing' bitmasks in PHP and thinking they will work in JavaScript )

Please read the comments and implement @Paebbels solution. The difference in performance is almost 7x, so really only use this if it will not be used very often.

It utilizes base_convert to convert the base 10 integer to base 2, splits the string into a character array, reverses the array and then iterates over it looking for 1s.

$mask = 49152; // 0xC000

// Find which positions in the mask contain a '1'
$bitArray = array_reverse(str_split(base_convert($mask, 10, 2)));

foreach($bitArray as $k => $v) {
    if($v) {
        echo $k . " is a one\n";
    }
}

Output:

14 is a one

15 is a one

As a function:

function extractElements($mask, array $input) 
{
    $output = array();
    $bitArray = array_reverse(str_split(base_convert($mask, 10, 2)));

    foreach($bitArray as $k => $v) {
        if($v && isset($input[$k])) {
            $output[] = $input[$k];
        }
    }

    return $output;
}

$mask = 76; // 0x4C
$input = [
    'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight'
];

print_r(extractElements($mask, $input));

Output:

Array ( [0] => Three 1 => Four [2] => Seven )

I have implemented a function that fits your needs, and in additional:

  • can handle smaller or bigger data type

  • saving the values and not only the indexes


function extractMask($mask, &$idxs, &$values) {
    for($i = 0, $valueToCompare = 1; $valueToCompare <= $mask; $i++, $valueToCompare <<= 1) {
        if ($mask & $valueToCompare){ 
            $idxs[] = $i;
            $values[] = $valueToCompare;
        }
    }
}

// usage:
extractMask(49152, $idxs, $values);

// results:
var_dump($idxs);
var_dump($values);

For impression, here is a JS snippet:

 function extractMask(mask) { var result = {idxs: [], values: []}; for(i = 0, valueToCompare = 1; valueToCompare <= mask; i++, valueToCompare <<= 1) { if (mask & valueToCompare){ result.idxs.push(i); result.values.push(valueToCompare); } } return result; } //====================== UI ========================== var anchor = document.getElementsByTagName("a")[0]; var input = document.getElementsByTagName("input")[0]; anchor.addEventListener("click", function(e) { var result = extractMask(input.value); console.log(result); }); input.addEventListener("keyup", function (e) { if (e.keyCode == 13) { var result = extractMask(input.value); console.log(result); } }); //==================================================== 
 <input value="49152" /> <a href="#">calculate</a> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM