Reputation: 4584
I am trying to parse a time expression string into an associative array with full-word keys.
My input:
$time = "1d2h3m";
My desired output:
array(
"day" => 1,
"hour" => 2,
"minutes" => 3
)
I have tried to extract the numbers with explode()
.
$time = "1d2h3m";
$day = explode("d", $time);
var_dump($day); // 0 => string '1' (length=1)
// 1 => string '2h3m' (length=4)
How can I convert my strictly formatted string into the desired associative array?
Upvotes: 11
Views: 2150
Reputation: 12101
preg_split
returns an array of values split by pattern(#[dhm]#
).
list()
sets the value for each array element.
$d = [];
list($d['day'],$d['hour'],$d['minutes']) = preg_split('#[dhm]#',"1d2h3m");
Upvotes: 3
Reputation: 8689
One line code:
$result = array_combine(array("day", "hour", "minutes"), preg_split('/[dhm]/', "1d2h3m", -1, PREG_SPLIT_NO_EMPTY));
Upvotes: 0
Reputation: 47900
sscanf()
is certainly an ideal tool for this job. Unlike preg_match()
, this native text parsing function avoids creating a useless fullstring match. The numeric values that are extracted can be cast as integers and directly assigned to their respective keys in the result array.
Code (creates reference variables): (Demo)
$time = "1d2h3m";
sscanf($time, '%dd%dh%dm', $result['day'], $result['hour'], $result['minutes']);
var_export($result);
or if you are processing multiple strings in a loop and want to avoid creating reference variables (or find it ugly to unset()
the variables at the end of each iteration), then you can "destructure" the return array into the desired associative structure.
Code: (Demo)
$time = "1d2h3m";
[$result['day'], $result['hour'], $result['minutes']] = sscanf($time, '%dd%dh%dm');
var_export($result);
Output:
array (
'day' => 1,
'hour' => 2,
'minutes' => 3,
)
Upvotes: 0
Reputation: 1231
You should go with regex for this case
<?php
$time = "1d2h3m";
if(preg_match("/([0-9]+)d([0-9]+)h([0-9]+)m/i",$time,$regx_time)){
$day = (int) $regx_time[1];
$hour = (int) $regx_time[2];
$minute = (int) $regx_time[3];
var_dump($day);
}
?>
Explaination :
[0-9] : Match any digits between 0 and 9
[0-9]+ : Means, match digits at least one character
([0-9]+) : Means, match digits at least one character and capture the results
/........../i : Set the case insenstive for the pattern of regex you've setted
Regex is the way better to be a lexer and parse string lexically. And it's good to learn regex. Almost all programming language use regex
Upvotes: 16
Reputation: 4730
This answer is not 100% relevant to particular input string format from the question.
But it based on the PHP date parsing mechanism (is not my own date parsing bicycle).
PHP >=5.3.0 has
DateInterval
class.
You could create DateInterval objects from stings in two formats:
$i = new DateInterval('P1DT12H'); // ISO8601 format
$i = createFromDateString('1 day + 12 hours'); // human format
PHP official docs: http://php.net/manual/en/dateinterval.createfromdatestring.php
In ISO8601 format P
stands for "period". Format supports three forms of the periods:
Capital letters represents the following:
For example, "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
See https://en.wikipedia.org/wiki/ISO_8601#Durations for details.
Upvotes: 0
Reputation: 441
You can use this fn to get the formatted array which check for the string validation as well :
function getFormatTm($str)
{
$tm=preg_split("/[a-zA-z]/", $str);
if(count($tm)!=4) //if not a valid string
return "not valid string<br>";
else
return array("day"=>$tm[0],"hour"=>$tm[1],"minutes"=>$tm[2]);
}
$t=getFormatTm("1d2h3m");
var_dump($t);
Upvotes: 0
Reputation: 5395
There are a lot of great answers here, but I want to add one more, to show a more generic approach.
function parseUnits($input, $units = array('d'=>'days','h'=>'hours','m' => 'minutes')) {
$offset = 0;
$idx = 0;
$result = array();
while(preg_match('/(\d+)(\D+)/', $input,$match, PREG_OFFSET_CAPTURE, $offset)) {
$offset = $match[2][1];
//ignore spaces
$unit = trim($match[2][0]);
if (array_key_exists($unit,$units)) {
// Check if the unit was allready found
if (isset($result[$units[$unit]])) {
throw new Exception("duplicate unit $unit");
}
// Check for corect order of units
$new_idx = array_search($unit,array_keys($units));
if ($new_idx < $idx) {
throw new Exception("unit $unit out of order");
} else {
$idx = $new_idx;
}
$result[$units[trim($match[2][0])]] = $match[1][0];
} else {
throw new Exception("unknown unit $unit");
}
}
// add missing units
foreach (array_keys(array_diff_key(array_flip($units),$result)) as $key) {
$result[$key] = 0;
}
return $result;
}
print_r(parseUnits('1d3m'));
print_r(parseUnits('8h9m'));
print_r(parseUnits('2d8h'));
print_r(parseUnits("3'4\"", array("'" => 'feet', '"' => 'inches')));
print_r(parseUnits("3'", array("'" => 'feet', '"' => 'inches')));
print_r(parseUnits("3m 5 d 5h 1M 10s", array('y' => 'years',
'm' => 'months', 'd' =>'days', 'h' => 'hours',
'M' => 'minutes', "'" => 'minutes', 's' => 'seconds' )));
print_r(parseUnits("3m 5 d 5h 1' 10s", array('y' => 'years',
'm' => 'months', 'd' =>'days', 'h' => 'hours',
'M' => 'minutes', "'" => 'minutes', 's' => 'seconds' )));
Upvotes: 0
Reputation: 199
You could use one line solution,
$time = "1d2h3m";
$day = array_combine(
array("day","hour","months") ,
preg_split("/[dhm]/", substr($time,0,-1) )
);
Upvotes: 0
Reputation: 199
$time = "1d2h3m";
$split = preg_split("/[dhm]/",$time);
$day = array(
"day" => $split[0]
"hour" => $split[1]
"minutes" => $split[2]
);
Upvotes: 0
Reputation: 475
We can also achieve this using str_replace() and explode() function.
$time = "1d2h3m";
$time = str_replace(array("d","h","m"), "*", $time);
$exp_time = explode("*", $time);
$my_array = array( "day"=>(int)$exp_time[0],
"hour"=>(int)$exp_time[1],
"minutes"=>(int)$exp_time[2] );
var_dump($my_array);
Upvotes: 0
Reputation: 1604
Use this simple implementation with string replace and array combine.
<?php
$time = "1d2h3m";
$time=str_replace(array("d","h","m")," " ,$time);
$time=array_combine(array("day","hour","minute"),explode(" ",$time,3));
print_r($time);
?>
Upvotes: 5
Reputation: 9782
This approach uses a regular expression to extract the values and an array to map the letters to the words.
<?php
// Initialize target array as empty
$values = [];
// Use $units to map the letters to the words
$units = ['d'=>'days','h'=>'hours','m'=>'minutes'];
// Test value
$time = '1d2h3m';
// Extract out all the digits and letters
$data = preg_match_all('/(\d+)([dhm])/',$time,$matches);
// The second (index 1) array has the values (digits)
$unitValues = $matches[1];
// The third (index 2) array has the keys (letters)
$unitKeys = $matches[2];
// Loop through all the values and use the key as an index into the keys
foreach ($unitValues as $k => $v) {
$values[$units[$unitKeys[$k]]] = $v;
}
Upvotes: 4
Reputation: 26385
A simple sscanf
will parse this into an array. Then you array_combine
it with the list of keys you want.
$time = "1d2h3m";
$result = array_combine(
['day', 'hour', 'minutes'],
sscanf($time, '%dd%dh%dm')
);
print_r($result);
Array
(
[day] => 1
[hour] => 2
[minutes] => 3
)
To break down the sscanf
format used:
%d
- reads a (signed) decimal number, outputs an integerd
- matches the literal character "d"%d
- (as the first)h
- matches the literal character "h"%d
- (as the first)m
- matches the literal character "m" (optional, as it comes after everything you want to grab)It'll also work fine with multiple digits and negative values:
$time = "42d-5h3m";
Array
(
[day] => 42
[hour] => -5
[minutes] => 3
)
Upvotes: 24
Reputation: 295
Can't you just explode it 3 times...
// Define an array
$day = explode("d", $time);
// add it in array with key as "day" and first element as value
$hour= explode("h", <use second value from above explode>);
// add it in array with key as "hour" and first element as value
$minute= explode("m", <use second value from above explode>);
// add it in array with key as "minute" and first element as value
I don't have any working example right now but I think it will work.
Upvotes: 0
Reputation: 7785
Customizable function, and the most important thing, that you can catch an exception if input is not properly formed
/**
* @param $inputs string : date time on this format 1d2h3m
* @return array on this format "day" => 1,
* "hour" => 2,
* "minutes" => 3
* @throws Exception
*
*/
function dateTimeConverter($inputs) {
// here you can customize how the function interprets input data and how it should return the result
// example : you can add "y" => "year"
// "s" => "seconds"
// "u" => "microsecond"
// key, can be also a string
// example "us" => "microsecond"
$dateTimeIndex = array("d" => "day",
"h" => "hour",
"m" => "minutes");
$pattern = "#(([0-9]+)([a-z]+))#";
$r = preg_match_all($pattern, $inputs, $matches);
if ($r === FALSE) {
throw new Exception("can not parse input data");
}
if (count($matches) != 4) {
throw new Exception("something wrong with input data");
}
$datei = $matches[2]; // contains number
$dates = $matches[3]; // contains char or string
$result = array();
for ($i=0 ; $i<count ($dates) ; $i++) {
if(!array_key_exists($dates[$i], $dateTimeIndex)) {
throw new Exception ("dateTimeIndex is not configured properly, please add this index : [" . $dates[$i] . "]");
}
$result[$dateTimeIndex[$dates[$i]]] = (int)$datei[$i];
}
return $result;
}
Upvotes: 13
Reputation: 695
You can do using this code.
<?php
$str = "1d2h3m";
list($arr['day'],$arr['day'],$arr['hour'],$arr['hour'],$arr['minute'],$arr['minute']) = $str;
print_r($arr);
?>
OUTPUT
Array (
[minute] => 3
[hour] => 2
[day] => 1
)
Upvotes: -2
Reputation: 12332
Another regex solution
$subject = '1d2h3m';
if(preg_match('/(?P<day>\d+)d(?P<hour>\d+)h(?P<minute>\d+)m/',$subject,$matches))
{
$result = array_map('intval',array_intersect_key($matches,array_flip(array_filter(array_keys($matches),'is_string'))));
var_dump($result);
}
Returns
array (size=3)
'day' => int 1
'hour' => int 2
'minute' => int 3
Upvotes: 5
Reputation: 657
I think for such a small string, and if the format will always be the same, you could use array_push
and substr
to get extract the numbers out of the string and put them in an array.
<?php
$time = "1d2h3m";
$array = array();
array_push($array, (int) substr($time, 0, 1));
array_push($array, (int) substr($time, 2, 1));
array_push($array, (int) substr($time, 4, 1));
var_dump($array);
?>
Upvotes: 4