tputkonen
tputkonen

Reputation: 5739

Parse string containing range of values to min and max variables

I'm looking for a nice way to parse a string into two variables using PHP. The variables are called minage and maxage, and they should be parsed according to the examples below:

"40" -> minage=40, maxage=40
"-40" -> minage=null, maxage=40
"40-" -> minage=40, maxage=null
"40-60" -> minage=40, maxage=60

Upvotes: 0

Views: 720

Answers (4)

mickmackusa
mickmackusa

Reputation: 48031

Here is a solution that will correctly handle 0 values in the range expression with only one preg_ function call.

I'll add a helper function which will cast non-null values to an integer data type. Of course, if this casting is not critical, it can be omitted and the extracted values will be null or string type.

Essentially, the regex makes all components in the string optional and gives priority to the max age when populating the array of matches. The hyphen is captured to determine whether the max age should be used to replace the null min age.

The null coalescing assignment operator (??=) means that the following condition is only executed if $minAge is null.

Code: (Demo)

$ranges = [
    "40",    // minage=40, maxage=40
    "-40",   // minage=null, maxage=40
    "40-",   // minage=40, maxage=null
    "40-60", // minage=40, maxage=60
    "0",     // minage=0, maxage=0
    "-0",    // minage=null, maxage=0
    "0-",    // minage=0, maxage=null
    "0-0"
];

function nullableInt(?int $v) { return $v; }

foreach ($ranges as $range) {
    [, $minAge, $hyphen, $maxAge] = preg_match('/^(\d+)??(-)?(\d+)?$/', $range, $m, PREG_UNMATCHED_AS_NULL) ? $m : [null, null, null, null];
    $minAge ??= ($hyphen ? null : $maxAge);
    var_export(['minAge' => nullableInt($minAge), 'maxAge' => nullableInt($maxAge)]);
    echo "\n";
}

Output:

array (
  'minAge' => 40,
  'maxAge' => 40,
)
array (
  'minAge' => NULL,
  'maxAge' => 40,
)
array (
  'minAge' => 40,
  'maxAge' => NULL,
)
array (
  'minAge' => 40,
  'maxAge' => 60,
)
array (
  'minAge' => 0,
  'maxAge' => 0,
)
array (
  'minAge' => NULL,
  'maxAge' => 0,
)
array (
  'minAge' => 0,
  'maxAge' => NULL,
)
array (
  'minAge' => 0,
  'maxAge' => 0,
)

Upvotes: 0

Bart Kiers
Bart Kiers

Reputation: 170278

You could also encapsulate the data in a class, say Range:

class Range {

  protected $min;
  protected $max;

  public function __construct($str) {
    if(preg_match('/^\d+$/', $str)) {
      $this->min = (int)$str;
      $this->max = (int)$str;
    } else {
      preg_match('/^(\d*)-(\d*)$/', $str, $matches);
      $this->min = $matches[1] ? (int)$matches[1] : null;
      $this->max = $matches[2] ? (int)$matches[2] : null;
    }
  }

  // more functions here like contains($value) and/or min() and max()

  public function __toString() {
    return 'min=' . $this->min . ', max=' . $this->max;
  }
}

$tests = array('40', '-40', '40-', '40-60');
foreach($tests as $t) {
  echo new Range($t) . "\n";
}

which produces:

min=40, max=40
min=, max=40
min=40, max=
min=40, max=60

Of course, you could replace the preg_ calls with some "normal" string functions, but the only thing I know of PHP is some regex-trickery.

Upvotes: 2

Gumbo
Gumbo

Reputation: 655619

Try this:

$minrange = null;
$maxrange = null;
$parts = explode('-', $str);
switch (count($parts)) {
case 1:
    $minrange = $maxrange = intval($parts[0]);
    break;
case 2:
    $minrange = $parts[0] == "" ? null : intval($parts[0]);
    $maxrange = $parts[1] == "" ? null : intval($parts[1]);
    break;
}

Upvotes: 4

Dominic Rodger
Dominic Rodger

Reputation: 99821

$parts = explode("-", $str);
$minage = NULL;
$maxage = NULL;
if (count($parts) == 1) {
  $minage = intval($parts[0]);
  $maxage = $minage;
}
else if ((count($parts) >= 2) && is_numeric($parts[0]) && is_numeric($parts[1])) {
  $minage = intval($parts[0]);
  $maxage = intval($parts[1]);
}

Upvotes: 0

Related Questions