Bitmask to array

Viewed 61

I need to get all weekdays from bitmask. Is there some function to decompose the bitmask?

For example:

const MONDAY    = 1 << 0; // 1
const TUESSAY   = 1 << 1; // 2
const WEDNESDAY = 1 << 2; // 4
const THURSSAY  = 1 << 3; // 8
const FRIDAY    = 1 << 4; // 16
const SATURDAY  = 1 << 5; // 32
const SUNDAY    = 1 << 6; // 64

function bitmaskToArray(int $bitmask): array
{
    //...
}

$wednesdayAndMonday = WEDNESDAY | MONDAY;

var_dump(bitmaskToArray($wednesdayAndMonday));

I see one solution, but I believe it's not the best one:

const WEEKDAYS = [
    MONDAY,
    TUESSAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY,
];

function bitmaskToArray(int $bitmask): array
{
    $result = [];
    foreach (WEEKDAYS as $weekday) {
        if ($bitmask & $weekday) {
            $result []= $weekday;
        }
    }
    
    return $result;
}
3 Answers

You can loop over the bit numbers and calculate the bit shift in the loop.

function bitmaskToArray(int $bitmask, int $maxbits): array
{
    $result = [];
    for ($thisbit = 1; $i < (1 < $maxbits); $thisbit <<= 1) {
        if ($bitmask & $thisbit) {
            $result[] = $thisbit;
        }
    }
    
    return $result;
}

Then you can call bitmaskToArray($wednesdayAndMonday, count(WEEKDAYS))

Honestly I'm not an expert in algorithm. My best bet is to add a cache as follows. You probably can get rid of the "global" by creating a class to handle the logic (which hides the global cache inside the class)

<?php

const MONDAY = 1 << 0; // 1
const TUESSAY = 1 << 1; // 2
const WEDNESDAY = 1 << 2; // 4
const THURSSAY = 1 << 3; // 8
const FRIDAY = 1 << 4; // 16
const SATURDAY = 1 << 5; // 32
const SUNDAY = 1 << 6; // 64

const WEEKDAYS = [
    MONDAY => "Monday",
    TUESSAY => "Tuesday",
    WEDNESDAY => "Wednesday",
    THURSDAY => "Thursday",
    FRIDAY => "Friday",
    SATURDAY => "Saturday",
    SUNDAY => "Sunday",
];

global $cache;
$cache = array();
function bitmaskToArray($bitmask)
{
    $result = [];
    global $cache;
    if (array_key_exists($bitmask, $cache)) {
        echo "Found cache: ";
        var_dump($cache[$bitmask]);
        return cache[$bitmask];
    }
    foreach (WEEKDAYS as $value => $weekday) {
        if ($bitmask & $value) {
            $result [] = $weekday;
        }
    }
    // save to cache
    $cache[$bitmask] = $result;
    return $result;
}

bitmaskToArray(5);
bitmaskToArray(5);

I came up with another solution not tied to the number of options. But it does not seem the best either:

function bitmaskToArray(int $bitmask): array
{
    $result = [];
    $bits = str_split(base_convert($bitmask, 10, 2));
    $c = count($bits);
    foreach ($bits as $i => $bit) {
        if ($bit != '0') {
            $result []= base_convert($bit . str_repeat('0', $c-($i+1)), 2, 10);
        }
    }
    
    return $result;
}
Related