24
$split_point = ' - ';
$string = 'this is my - string - and more';

How can I make a split using the second instance of $split_point and not the first one. Can I specify somehow a right to left search?

Basically how do I explode from right to left. I want to pick up only the last instance of " - ".

Result I need:

$item[0]='this is my - string';
$item[1]='and more';

and not:

$item[0]='this is my';
$item[1]='string - and more';
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Codex73
  • 5,690
  • 11
  • 56
  • 76

13 Answers13

33

You may use strrev to reverse the string, and then reverse the results back:

$split_point = ' - ';
$string = 'this is my - string - and more';

$result = array_map('strrev', explode($split_point, strrev($string)));

Not sure if this is the best solution though.

moff
  • 6,415
  • 31
  • 30
  • dont quite also get the array_map usage – Codex73 Apr 05 '09 at 00:03
  • array_map runs strrev over every element in the array, and returns the result. You may also try something like this, which may be simpler: $result = array_reverse(explode($split_point, $string)); – moff Apr 05 '09 at 08:36
  • This is good solution, relatively fast. Slower than explode but faster than my solution bellow. I tried this in a for loop of 1000 times and got 0.046875s. – John Boe Oct 19 '19 at 06:18
13

How about this:

$parts = explode($split_point, $string);
$last = array_pop($parts);
$item = array(implode($split_point, $parts), $last);

Not going to win any golf awards, but it shows intent and works well, I think.

Paolo Bergantino
  • 480,997
  • 81
  • 517
  • 436
9

Here is another way of doing it:

$split_point = ' - ';
$string = 'this is my - string - and more';

$stringpos = strrpos($string, $split_point, -1);
$finalstr = substr($string,0,$stringpos);
antyrat
  • 27,479
  • 9
  • 75
  • 76
5

If I understand correctly, you want the example case to give you ('this is my - string', 'and more')?

Built-in split/explode seems to be forwards-only - you'll probably have to implement it yourself with strrpos. (right-left search)

$idx = strrpos($string, $split_point);
$parts = array(substr($string, 0, $idx), substr($string, $idx+strlen($split_point)))
v3.
  • 2,003
  • 15
  • 18
2

I am underwhelmed by all of the over-engineered answers which are calling many functions and/or iterating over data multiple times. All of those string and array reversing functions make the technique very hard to comprehend.

Regex is powerfully elegant in this case. This is exactly how I would do it in a professional application:

Code: (Demo)

$string = 'this is my - string - and more';
var_export(preg_split('~.*\K - ~', $string));

Output:

array (
  0 => 'this is my - string',
  1 => 'and more',
)

By greedily matching characters (.*), then restarting the fullstring match (\K), then matching the last occurring delimiting substring (" - "), you are assured to only separate the final substring from the string.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • 1
    unreadable is not `powerfully elegant` – raveren Apr 05 '23 at 07:37
  • I find this 7-character pattern in a single function approach to be very elegant. If you don't know what `*`, `\K`, and " - " do, then my answer explains these basic subpatterns. – mickmackusa Apr 05 '23 at 07:44
  • 1
    I agree this is a good use case for regex, and is far cleaner than the horrible approaches of operating on strings using convoluted combinations of array functions. People are afraid of regex, and it is indeed sometimes incredibly unreadable, but in this case it's short, simple, and works perfectly. – BadHorsie May 31 '23 at 10:57
2

Why not split on ' - ', but then join the first two array entries that you get back together?

Alister Bulman
  • 34,482
  • 9
  • 71
  • 110
  • but i need to split on the last instance always of -. – Codex73 Apr 04 '09 at 16:38
  • if it is always going to be the last instance, you can find the last match with http://php.net/strrpos, then that whichever side you want to/from that position with substr – Alister Bulman Apr 04 '09 at 16:45
1

I liked Moff's answer, but I improved it by limiting the number of elements to 2 and re-reversing the array:

$split_point = ' - ';
$string = 'this is my - string - and more';

$result = array_reverse(array_map('strrev', explode($split_point, strrev($string),2)));

Then $result will be :

Array ( [0] => this is my - string [1] => and more )
Coomie
  • 4,832
  • 3
  • 32
  • 44
1

this is my - string - and more

  1. Use common explode function to get all strings
  2. Get sizeof array and fetch last item
  3. Pop last item using array_pop function.
  4. Implode remaining string with same delimeter(if u want other delimeter can use).

Code

$arrSpits=explode("", "this is my - string - and more");
$arrSize=count($arrSpits);

echo "Last string".$arrSpits[arrSize-1];//Output: and more


array_pop(arrSpits); //now pop the last element from array

$firstString=implode("-", arrSpits);
echo "First String".firstString; //Output: this is my - string
Deepa MG
  • 188
  • 1
  • 15
1

I've stumbled uppon the same, and fixed it like so:

$split_point = ' - ';
$string = 'this is my - string - and more';

$reverse_explode = array_reverse(explode($split_point, $string));
Dennis
  • 780
  • 6
  • 17
0

Just an idea:

function explode_reversed($delim,$s){
  $result = array();
  $w = "";
  $l = 0;
  for ($i = strlen($s)-1; $i>=0; $i-- ):
    if ( $s[$i] == "$delim" ):
      $l++;
      $w = "";
    else:
      $w = $s[$i].$w;
    endif;
    $result[$l] = $w;
  endfor;
return $result;
}
$arr = explode_reversed(" ","Hello! My name is John.");
print_r($arr);

Result:

Array
(
    [0] => John.
    [1] => is
    [2] => name
    [3] => My
    [4] => Hello!
)

But this is much slower then explode. A test made:

$start_time = microtime(true);
for ($i=0; $i<1000;$i++)   
  $arr = explode_reversed(" ","Hello! My name is John.");
$time_elapsed_secs = microtime(true) - $start_time;
echo "time: $time_elapsed_secs s<br>";

Takes 0.0625 - 0.078125s

But

for ($i=0; $i<1000;$i++)   
  $arr = explode(" ","Hello! My name is John.");

Takes just 0.015625s

The fastest solution seems to be:

array_reverse(explode($your_delimiter, $your_string));

In a loop of 1000 times this is the best time I can get 0.03125s.

John Boe
  • 3,501
  • 10
  • 37
  • 71
0

Not sure why no one posted a working function with $limit support though here it is for those who know that they'll be using this frequently:

<?php
function explode_right($boundary, $string, $limit)
{
 return array_reverse(array_map('strrev', explode(strrev($boundary), strrev($string), $limit)));
}

$string = 'apple1orange1banana1cupuacu1cherimoya1mangosteen1durian';
echo $string.'<br /><pre>'.print_r(explode_right(1, $string, 3),1).'</pre>';
?>
John
  • 1
  • 13
  • 98
  • 177
0

Assuming you only want the first occurrence of $split_point to be ignored, this should work for you:

# retrieve first $split_point position
$first = strpos($string, $split_point);
# retrieve second $split_point positon
$second = strpos($string, $split_point, $first+strlen($split_point));
# extract from the second $split_point onwards (with $split_point)
$substr = substr($string, $second);

# explode $substr, first element should be empty
$array = explode($split_point, $substr);

# set first element as beginning of string to the second $split_point
$array[0] = substr_replace($string, '', strpos($string, $substr));

This will allow you to split on every occurrence of $split_point after (and including) the second occurrence of $split_point.

quaff
  • 11
  • 2
-1
$split_point = ' - ';
$string = 'this is my - string - and more';
$result = end(explode($split_point, $string));

This is working fine

Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71