Explode only by last delimiter - php

is there a way to use explode function to explode only by last delimiter occurrence?
$string = "one_two_ ... _three_four";
$explodeResultArray = explode("_", $string);
Result Should Be:
echo $explodeResultArray[0]; // "one_two_three ...";
echo $explodeResultArray[1]; // "four";

Straightforward:
$parts = explode('_', $string);
$last = array_pop($parts);
$parts = array(implode('_', $parts), $last);
echo $parts[0]; // outputs "one_two_three"
Regular expressions:
$parts = preg_split('~_(?=[^_]*$)~', $string);
echo $parts[0]; // outputs "one_two_three"
String reverse:
$reversedParts = explode('_', strrev($string), 2);
echo strrev($reversedParts[0]); // outputs "four"

There is no need for a workaround. explode() accepts a negative limit.
$string = "one_two_three_four";
$part = implode('_', explode('_', $string, -1));
echo $part;
Result is
one_two_three

I chose to use substring becasue you want a string up to a particular point:
$string = "one_two_three_four_five_six_seven";
$part1 = substr("$string",0, strrpos($string,'_'));
$part2 = substr("$string", (strrpos($string,'_') + 1));
var_dump($part1,$part2);
RESULTS:
string(27) "one_two_three_four_five_six"
string(5) "seven"

You could do the following:
$string = "one_two_three_four";
$explode = explode('_', $string); // split all parts
$end = '';
$begin = '';
if(count($explode) > 0){
$end = array_pop($explode); // removes the last element, and returns it
if(count($explode) > 0){
$begin = implode('_', $explode); // glue the remaining pieces back together
}
}
EDIT:
array_shift should have been array_pop

<?php
$lastPos = strrpos($string, '_');
if ($lastPos !== false) {
$start = substr($string, 0, $lastPos);
$end = substr($string, $lastPos+1);
} else {
// no delimeter found!
}
If you only care about the last part, it's even simpler.
<?php
$end = substr(strrchr($string, '_'), 1);

Use preg_match()
$string = "one_two_three_four";
$arr = array();
preg_match("/(^.*)_(.*?)$/", $string, $arr);
print_r($arr);
Output: Array ( [0] => one_two_three_four [1] => one_two_three [2] => four )

use end + explode
$explodeResultArray = end(explode("_", $string));
$explodeResultArray will = four

// reverse $string right after definition
$string = "one_two_three_four_five_six";
$string = implode("_",array_reverse(explode("_",$string)));
// chop off the first part
list($result, $string) = explode("_", $string, 2);
echo "$result --- $string";
Output:
six --- five_four_three_two_one

$explodeResultArray = explode("_", $string);
$last_item = end($explodeResultArray);
$key = count($explodeResultArray) - 1;
unset($explodeResultArray[$key]);
$arr[] = (implode($explodeResultArray,'_'));
$arr[] = $last_item;
print_r($arr);
Output
Array
(
[0] => one_two_ ... _three
[1] => four
)

I had similar needs and inspired by #NLZ's answer I've made a reusable function with the same features as regular explode(), but backwards (although I added an option to reverse the array order contra regular explode()):
function backward_explode($delimiter, $string, $limit = null, $keep_order = true) {
if ((string)$delimiter === "") {
return false;
}
if ($limit === 0 || $limit === 1) {
return array($string);
}
$explode = explode($delimiter, $string);
if ($limit === null || $limit === count($explode)) {
return $keep_order? $explode : array_reverse($explode);
}
$parts = array();
if ($limit > 0) {
for ($i = 1; $i < $limit; $i++) {
$parts[] = array_pop($explode);
}
$remainder = implode($delimiter, $explode);
$parts[] = $remainder;
if ($keep_order) {
$parts = array_reverse($parts);
}
} else {
if (strpos($string, $delimiter) === false) {
return array();
}
$parts = $explode;
array_splice($parts, 0, abs($limit));
if (!$keep_order) {
$parts = array_reverse($parts);
}
}
return $parts;
}
(Also as a gist.)
So with:
$string = 'one two three four';
var_dump(backward_explode(' ', $string));
var_dump(backward_explode(' ', $string, 2));
var_dump(backward_explode(' ', $string, 3));
var_dump(backward_explode(' ', $string, 2, false));
var_dump(backward_explode(' ', $string, -1));
var_dump(backward_explode(' ', $string, 1)); // same as with $limit = 0
var_dump(backward_explode('#', $string, -2));
var_dump(backward_explode('', $string, 3));
We get:
array (size=4)
0 => string 'one' (length=3)
1 => string 'two' (length=3)
2 => string 'three' (length=5)
3 => string 'four' (length=4)
array (size=2)
0 => string 'one two three' (length=13)
1 => string 'four' (length=4)
array (size=3)
0 => string 'one two' (length=7)
1 => string 'three' (length=5)
2 => string 'four' (length=4)
array (size=2)
0 => string 'four' (length=4)
1 => string 'one two three' (length=13)
array (size=3)
0 => string 'two' (length=3)
1 => string 'three' (length=5)
2 => string 'four' (length=4)
array (size=1)
0 => string 'one two three four' (length=18)
array (size=0)
empty
boolean false

For such a taks, you can just use strstr and strrchr:
$valueBeforeLastUnderscore = rtrim(strrev(strstr(strrev($value), '_')), '_');
$valueAfterLastUnderscore = ltrim(strrchr($value, '_'), '_');
That being said, I like the regular expression answer more.

simply use sub string
substr($string ,stripos($string ,'_'))

Related

exploding string with multiple delimiter [duplicate]

This question already has answers here:
PHP - split String in Key/Value pairs
(5 answers)
Convert backslash-delimited string into an associative array
(4 answers)
Closed 12 months ago.
i have a string like
$str = "1-a,2-b,3-c";
i want to convert it into a single array like this
$array = [
1 => "a",
2 => "b",
3 => "c"
];
what i do is
$str = "1-a,2-b,3-c";
$array = [];
$strex = explode(",", $str);
foreach ($strex as $str2) {
$alphanumeric = explode("-", $str2);
$array[$alphanumeric[0]] = $alphanumeric[1];
}
can i do this in a better way?
You can use preg_match_all for this:
<?php
$str = "1-a,2-b,3-c";
preg_match_all('/[0-9]/', $str, $keys);
preg_match_all('/[a-zA-Z]/', $str, $values);
$new = array_combine($keys[0], $values[0]);
echo '<pre>'. print_r($new, 1) .'</pre>';
here we take your string, explode() it and then preg_match_all the $value using patterns:
/[0-9]/ -> numeric value
/[a-zA-Z]/ -> letter
then use array_combine to get it into one array
Thanks to u_mulder, can shorten this further:
<?php
$str = "1-a,2-b,3-c";
preg_match_all('/(\d+)\-([a-z]+)/', $str, $matches);
$new = array_combine($matches[1], $matches[2]);
echo '<pre>'. print_r($new, 1) .'</pre>';
just a little benchmark:
5000 iterations
Debian stretch, php 7.3
parsed string: "1-a,2-b,3-c,4-d,5-e,6-f,7-g,8-h,9-i"
[edit] Updated with the last 2 proposals [/edit]
You can use preg_split with array_filter and array_combine,
function odd($var)
{
// returns whether the input integer is odd
return $var & 1;
}
function even($var)
{
// returns whether the input integer is even
return !($var & 1);
}
$str = "1-a,2-b,3-c";
$temp = preg_split("/(-|,)/", $str); // spliting with - and , as said multiple delim
$result =array_combine(array_filter($temp, "even", ARRAY_FILTER_USE_KEY),
array_filter($temp, "odd",ARRAY_FILTER_USE_KEY));
print_r($result);
array_filter — Filters elements of an array using a callback function
Note:- ARRAY_FILTER_USE_KEY - pass key as the only argument to callback instead of the value
array_combine — Creates an array by using one array for keys and another for its values
Demo
Output:-
Array
(
[1] => a
[2] => b
[3] => c
)
One way to do with array_map(),
<?php
$my_string = '1-a,2-b,3-c';
$my_array = array_map(function($val) {list($key,$value) = explode('-', $val); return [$key=>$value];}, explode(',', $my_string));
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($my_array)) as $k=>$v){
$result[$k]=$v;
}
print_r($result);
?>
WORKING DEMO: https://3v4l.org/aYmOH
Tokens all the way down...
<?php
$str = '1-a,2-b,3-c';
$token = '-,';
if($n = strtok($str, $token))
$array[$n] = strtok($token);
while($n = strtok($token))
$array[$n] = strtok($token);
var_export($array);
Output:
array (
1 => 'a',
2 => 'b',
3 => 'c',
)
Or perhaps more terse without the first if...:
$array = [];
while($n = $array ? strtok($token) : strtok($str, $token))
$array[$n] = strtok($token);
Not a better way but one more example:
$str = "1-a,2-b,3-c";
$arr1 = explode(",", preg_replace("/\-([a-zA-Z]+)/", "", $str));
$arr2 = explode(",", preg_replace("/([0-9]+)\-/", "", $str));
print_r(array_combine($arr1, $arr2));
Mandatory one-liner (your mileage may vary):
<?php
parse_str(str_replace([',', '-'], ['&', '='], '1-a,2-b,3-c'), $output);
var_export($output);
Output:
array (
1 => 'a',
2 => 'b',
3 => 'c',
)
You can do one split on both the , and -, and then iterate through picking off every other pair ($k&1 is a check for an odd index):
<?php
$str = '1-a,2-b,3-c';
foreach(preg_split('/[,-]/', $str) as $k=>$v) {
$k&1 && $output[$last] = $v;
$last = $v;
}
var_export($output);
Output:
array (
1 => 'a',
2 => 'b',
3 => 'c',
)
The preg_split array looks like this:
array (
0 => '1',
1 => 'a',
2 => '2',
3 => 'b',
4 => '3',
5 => 'c',
)
This one explodes the string as the OP has on the comma, forming the pairs: (1-a) and (2-b) etc. and then explodes those pairs. Finally array_column is used to create the associated array:
<?php
$str = '1-a,2-b,3-c';
$output =
array_column(
array_map(
function($str) { return explode('-', $str); },
explode(',', $str)
),
1,
0
);
var_export($output);
Output:
array (
1 => 'a',
2 => 'b',
3 => 'c',
)

Extract numbers separated by special char from string

If I have a string like:
123*23*6594*2*-10*12
How can I extract the single numbers, in the string separated by *? That is, I want this output:
a=123, b=23, c=6594, d=2, e=-10, f=12.
Flexible:
$vars = range('a', 'z');
$vals = explode('*', $string);
$result = array_combine(array_slice($vars, 0, count($vals)), $vals);
Result:
Array
(
[a] => 123
[b] => 23
[c] => 6594
[d] => 2
[e] => -10
[f] => 12
)
Just for the sheer fun of setting the result as an iterable (rather than an actual array) with the alpha keys in a slightly different manner:
$data = '123*23*6594*2*-10*12';
function dataseries($data) {
$key = 'a';
while (($x = strpos($data, '*')) !== false) {
yield $key++ => substr($data, 0, $x);
$data = substr($data, ++$x);
}
yield $key++ => $data;
}
foreach(dataseries($data) as $key => $value) {
echo $key, ' = ', $value, PHP_EOL;
}
Requires PHP >= 5.5.0
You can use simple as this :
$str = "123*23*6594*2*-10*12";
$arr = explode("*",$str);
$arr['a']=$arr[0];
$arr['b']=$arr[1];
$arr['c']=$arr[2];
$arr['d']=$arr[3];
$arr['e']=$arr[4];
$arr['f']=$arr[5];
echo "a=" .$arr['a'] ." b=". $arr['b']." c=". $arr['c']." d=". $arr['d']." e=". $arr['e']." f=". $arr['f'];
EDIT:
To accomplish Rizier123 wish :
$str = "123*23*6594*2*-10*12";
$arr = explode("*",$str);
$vars = range('a', 'z');
foreach ($arr as $val => $key){
echo $vars[$val]."=".$key."<br>";
}
something like:
list($a,$b,$c,$d,$e,$f) = explode("*", $str);
Just a guess ;)

Split a string into keys of associative arrays (PHP)

Do you have an idea, how can a string be converted to a variable, e.g.
there is a string -> $string = 'one|two|three';
there is an array -> $arr = array('one' => array('two' => array('three' => 'WELCOME')));
I want to use all with explode(); exploded values to access the array $arr. I tried this code:
$c = explode('|', $string);
$str = 'arr[' . implode('][', $c) . ']';
echo $$str;
It doesnt work, sadly :( Any ideas?
You're doing it wrong.
You can do what you want with a loop to go through the array level by level
$string = 'one|two|three';
$arr = array('one' => array('two' => array('three' => 'WELCOME')));
$c = explode('|', $string);
$result = $arr;
foreach($c as $key)
$result = $result[$key];
echo $result; // WELCOME
Here's a sort of recursive function:
$ex_keys = array('one', 'two', 'three');
$ex_arr = array('one' => array('two' => array('three' => 'WELCOME')));
function get_recursive_var($keys, $arr) {
if (sizeof($keys) == 1)
return $arr[$keys[0]];
else {
$key = array_shift($keys);
return get_recursive_var($keys, $arr[$key]);
}
}
var_dump(get_recursive_var($ex_keys, $ex_arr));

Split string on multiple characters in PHP

I need to split an age into its components where the age is expressed as eg. 27y5m6w2d or any combination of those values. eg. 2w3d or 27d or 5y2d etc. Result has to be up to 4 variables $yrs, $mths, $wks and $days containing the appropriate numeric values.
I can do it with this code but am hoping there is something more efficient:
$pos = strpos($age, 'y');
if ($pos !== false)
list($yrs, $age) = explode('y', $age);
$pos = strpos($age, 'm');
if ($pos !== false)
list($mths, $age) = explode('m', $age);
$pos = strpos($age, 'w');
if ($pos !== false)
list($wks, $age) = explode('w', $age);
$pos = strpos($age, 'd');
if ($pos !== false)
list($days, $age) = explode('d', $age);
If you have a suggestion, please run it in a 10,000 iteration loop and advise the results. The code above runs in an average of 0.06 seconds for 10,000 iterations. I use this code to test:
<?php
$startTime = microtime(true);
// code goes here
echo "Time: " . number_format(( microtime(true) - $startTime), 4) . " Seconds<br>";
echo 'y='.$yrs.' m='.$mths.' w='.$wks.' d='.$days;
?>
I would suggest using regular expression matching with preg_match_all() like this:
$input = '2w3d'
$matches = array();
preg_match_all('|(\d+)([ymwd])|', $input, $matches, PREG_SET_ORDER);
Where the output array $matches will hold all matches in this pattern:
$matches = array(
// 0 => matched string, 1 => first capture group, 2 => second capture group
0 => array( 0 => '2w', 1 => '2', 2 => 'w' ),
1 => array( 0 => '3d', 1 => '3', 2 => 'd' )
);
EDIT :
Process this result like so:
$yrs = $mths = $wks = $days = 0;
foreach($matches as $match) {
switch($match[2]) {
case 'y': $yrs = (int)$match[1]; break;
case 'm': $mths = (int)$match[1]; break;
case 'w': $wkss = (int)$match[1]; break;
case 'd': $days = (int)$match[1]; break;
}
}
EDIT 2: Hacky alternative
Makes use of character comparison and takes around 0.4 seconds for 100.000 iterations.
$number = '';
for($j = 0, $length = strlen($input); $j < $length; $j++) {
if($input[$j] < 'A') {
$number .= $input[$j];
} else {
switch($input[$j]) {
case 'y': $yrs = (int)$number; break;
case 'm': $mths = (int)$number; break;
case 'w': $wks = (int)$number; break;
case 'd': $days = (int)$number; break;
}
$number = '';
}
}
I'd go with the following approach.
$age = '27y5m6w2d';
// Split the string into array of numbers and words
$arr = preg_split('/(?<=[ymdw])/', $age, -1, PREG_SPLIT_NO_EMPTY);
foreach ($arr as $str)
{
$item = substr($str, -1); // Get last character
$value = intval($str); // Get the integer
switch ($item)
{
case 'y':
$year = $value;
break;
case 'm':
$month = $value;
break;
case 'd':
$day = $value;
break;
case 'w':
$week = $value;
break;
}
}
The code is more readable and slightly faster. I tested this with 10000 iterations and it took around just 0.0906 seconds.
You don't need to bloat your code with a lookup array or a switch block.
Your input strings are predictably formatted (in order), so you can write a single regex pattern containing optional capture groups at each of the expected "units" in the input string. While using named capture groups provides some declarative benefit, it also bloats the regex pattern and the output array -- so I generally don't prefer to use them.
You will notice that there is a repeated format in the regex: (?:(\d+)unitLetter)?. This makes modifying/extending the pattern very simple. All of those subpatterns make the targeted substring "optional" and the final letter in the subpattern distinguishes what unit of time is being isolated.
In this case, the match output structure goes:
[0] : the full string match (we don't need it)
[1] : yrs
[2] : mths
[3] : wks
[4] : days
Code: (Demo)
$strings = ['27y5m6w2d', '1m1w', '2w3d', '999y3w', '27d', '5y2d'];
foreach ($strings as $string) {
preg_match('~(?:(\d+)y)?(?:(\d+)m)?(?:(\d+)w)?(?:(\d+)d)?~', $string, $m);
var_export([
'yrs' => $m[1] ?? '',
'mths' => $m[2] ?? '',
'wks' => $m[3] ?? '',
'days' => $m[4] ?? '',
]);
echo "\n---\n";
}
Output:
array (
'yrs' => '27',
'mths' => '5',
'wks' => '6',
'days' => '2',
)
---
array (
'yrs' => '',
'mths' => '1',
'wks' => '1',
'days' => '',
)
---
array (
'yrs' => '',
'mths' => '',
'wks' => '2',
'days' => '3',
)
---
array (
'yrs' => '999',
'mths' => '',
'wks' => '3',
'days' => '',
)
---
array (
'yrs' => '',
'mths' => '',
'wks' => '',
'days' => '27',
)
---
array (
'yrs' => '5',
'mths' => '',
'wks' => '',
'days' => '2',
)
---

'cascading' explode string in php

How to explode this string :
00004.00001.00003.00001.00003
in an array like this :
array (size=3)
0 => string '00004' (length=5)
1 => string '00004.00001' (length=10)
2 => string '00004.00001.00003' (length=15)
3 => string '00004.00001.00003.00001.00003' (length=20)
Thx
$array = explode('.', '00004.00001.00003.00001.00003');
foreach($array as $key => $value) {
if($array[$key - 1]) {
$array[$key] = $array[$key - 1] . '.' . $value;
}
}
Explode it first as you would normally do $arr = explode('.', $str); and then build desired array with two loops.
Another version using array_map() :
$arr = explode('.', '00004.00001.00003.00001.00003');
$arr[] = 'blank';
$array = array_map(function(){
global $arr;
array_pop($arr);
return implode($arr);
}, $arr);

Categories