42

PHP's explode function returns an array of strings split on some provided substring. It will return empty strings when there are leading, trailing, or consecutive delimiters, like this:

var_dump(explode('/', '1/2//3/'));
array(5) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(0) ""
  [3]=>
  string(1) "3"
  [4]=>
  string(0) ""
}

Is there some different function or option or anything that would return everything except the empty strings?

var_dump(different_explode('/', '1/2//3/'));
array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(1) "3"
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Glenn Moss
  • 6,812
  • 6
  • 29
  • 23

12 Answers12

71

Try preg_split.

$exploded = preg_split('@/@', '1/2//3/', -1, PREG_SPLIT_NO_EMPTY);

Stan Derksen
  • 93
  • 12
ceejayoz
  • 176,543
  • 40
  • 303
  • 368
  • 1
    I think this is the most correct because it maintains the correct array indexes. – Glenn Moss Sep 15 '08 at 17:18
  • @Ryono: what do you mean by that, exactly? – Bobby Jack Oct 30 '08 at 15:17
  • 5
    preg_split will return an array with sequential array indexes, (i.e. 0, 1, 2, 3). Some of the other suggestions using array_filter will return non-sequential array indexes because some elements are filtered out and you're left with array indexes like 0,1,3,7. – Glenn Moss Aug 18 '09 at 16:23
  • 2
    @RafikBari The first character of a regex pattern is its delimiter. See http://php.net/manual/en/regexp.reference.delimiters.php. `/` is typically used, but I used `@` here as `/` was in our string. – ceejayoz Feb 08 '15 at 19:15
  • If the only reason is to get an array with non empty entries, you should use array_filter(explode()) option which is faster than preg_split. – Mert Simsek Nov 22 '18 at 22:30
  • @ceejay it would be better if the explanation of your snippet existed in the answer body instead of in the comments under the answer. – mickmackusa Mar 10 '21 at 12:29
  • @simsek calling `array_filter()` will also destroy `0` elements in the output -- a side effect that this answer doesn't suffer. – mickmackusa Mar 10 '21 at 12:30
  • @mickmackusa You have enough rep to propose an edit, if you desire. – ceejayoz Mar 10 '21 at 12:31
  • I wouldn't want to put words in your mouth. Your answer deserves your own personal explanation. – mickmackusa Mar 10 '21 at 12:31
  • @mickmackusa Feel free. That's why SO has an edit button. – ceejayoz Mar 10 '21 at 12:31
  • You are the one harvesting unicorn points from this code + link answer. Surely you can spare the time to role model better posting behaviors. – mickmackusa Mar 10 '21 at 12:36
  • I am comfortable with the state of the thirteen year old answer as it stands. – ceejayoz Mar 10 '21 at 15:05
  • I would make the pattern `@/+@` so that potentially longer matches are made, fewer replacements are needed, and the NO_EMPTY flag has less mopping up to do. – mickmackusa Jul 16 '22 at 07:55
  • @ceejayoz The third argument to `preg_split()` should be `-1` instead of `NULL`, as it accepts an integer. Especially important as we are moving towards typed variables in PHP 8 and higher. – Stan Derksen Sep 06 '22 at 11:14
32

array_filter will remove the blank fields, here is an example without the filter:

print_r(explode('/', '1/2//3/'))

prints:

Array
(
    [0] => 1
    [1] => 2
    [2] =>
    [3] => 3
    [4] =>
)

With the filter:

php> print_r(array_filter(explode('/', '1/2//3/')))

Prints:

Array
(
    [0] => 1
    [1] => 2
    [3] => 3
)

You'll get all values that resolve to "false" filtered out.

see http://uk.php.net/manual/en/function.array-filter.php

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Dave Gregory
  • 932
  • 7
  • 12
  • 10
    This will remove '0', too, as it is false in a boolean context. So it might not be what you want! – ThiefMaster Oct 23 '10 at 11:19
  • For clarity, "No" was just an answer to "Is there some different function or option or anything" -- I could have been clearer by saying "No, there's nothing built in." – Dave Gregory Oct 28 '10 at 16:32
  • This was just what I needed. I had to make sure a constant array always had four string values, which must be obtained from an explode. However, if the explode fails, I needed to provide a fallback value at [0]. I've achieved it this way: `array_filter(explode(',',($string, 4))) + ['default_value','','','']`. That way, a failed explode will contain no values, and the result will entirely be the second array. It's certainly unorthodox, but it's clean solution for a constant like this. – Super Cat Jan 29 '17 at 21:24
7

Just for variety:

array_diff(explode('/', '1/2//3/'), array(''))

This also works, but does mess up the array indexes unlike preg_split. Some people might like it better than having to declare a callback function to use array_filter.

Glenn Moss
  • 6,812
  • 6
  • 29
  • 23
  • There's no need for a callback function when you use array_filter if you're filtering just falsy values like empty string – alucic Jan 08 '16 at 22:55
5
function not_empty_string($s) {
  return $s !== "";
}

array_filter(explode('/', '1/2//3/'), 'not_empty_string');
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
James Aylett
  • 3,332
  • 19
  • 20
  • Note: this is better than the 'bare' call to array_filter() if you might have "1/2//0/3" and want to keep the zero. – Bobby Jack Sep 15 '08 at 16:54
  • Good comment. I Wanted to suggested the 'bare' call, but yes, it will throw out the zero. – Jrgns Sep 15 '08 at 22:04
1

I have used this in TYPO3, look at the $onlyNonEmptyValues parameter:

function trimExplode($delim, $string, $onlyNonEmptyValues=0){
    $temp = explode($delim,$string);
    $newtemp=array();
    while(list($key,$val)=each($temp))      {
        if (!$onlyNonEmptyValues || strcmp("",trim($val)))      {
            $newtemp[]=trim($val);
        }
    }
    reset($newtemp);
    return $newtemp;
}

It doesn't mess up the indexes:

var_dump(trimExplode('/', '1/2//3/',1));

Result:

array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [2]=>
  string(1) "3"
}
Memochipan
  • 3,405
  • 5
  • 35
  • 60
1

Here is a solution that should output a newly indexed array.

$result = array_deflate( explode( $delim, $array) );

function array_deflate( $arr, $emptyval='' ){
    $ret=[];
    for($i=0,$L=count($arr); $i<$L; ++$i)
        if($arr[$i] !== $emptyval) $ret[]=$arr[$i];
    return $ret;
}

While fairly similar to some other suggestion, this implementation has the benefit of generic use. For arrays with non-string elements, provide a typed empty value as the second argument.

array_deflate( $objArray, new stdClass() );

array_deflate( $databaseArray, NULL );

array_deflate( $intArray, NULL );

array_deflate( $arrayArray, [] );

array_deflate( $assocArrayArray, [''=>NULL] );

array_deflate( $processedArray, new Exception('processing error') );

.

.

.

With an optional filter argument..

function array_deflate( $arr, $trigger='', $filter=NULL, $compare=NULL){
    $ret=[];
    if ($filter === NULL) $filter = function($el) { return $el; };
    if ($compare === NULL) $compare = function($a,$b) { return $a===$b; };

    for($i=0,$L=count($arr); $i<$L; ++$i)
        if( !$compare(arr[$i],$trigger) ) $ret[]=$arr[$i];
        else $filter($arr[$i]);
    return $ret;
}

With usage..

function targetHandler($t){ /* .... */ }    
array_deflate( $haystack, $needle, targetHandler );

Turning array_deflate into a way of processing choice elements and removing them from your array. Also nicer is to turn the if statement into a comparison function that is also passed as an argument in case you get fancy.

array_inflate being the reverse, would take an extra array as the first parameter which matches are pushed to while non-matches are filtered.

function array_inflate($dest,$src,$trigger='', $filter=NULL, $compare=NULL){
    if ($filter === NULL) $filter = function($el) { return $el; };
    if ($compare === NULL) $compare = function($a,$b) { return $a===$b; };

    for($i=0,$L=count($src); $i<$L; ++$i)
        if( $compare(src[$i],$trigger) ) $dest[]=$src[$i];
        else $filter($src[$i]);
    return $dest;
}

With usage..

$smartppl=[];    
$smartppl=array_inflate( $smartppl,
                         $allppl,
                         (object)['intelligence'=>110],
                         cureStupid,
                         isSmart);

function isSmart($a,$threshold){
    if( isset($a->intellgence) )    //has intelligence?
        if( isset($threshold->intellgence) )    //has intelligence?
            if( $a->intelligence >= $threshold->intelligence )
                return true;
            else return INVALID_THRESHOLD; //error
        else return INVALID_TARGET; //error
    return false;
}

function cureStupid($person){
    $dangerous_chemical = selectNeurosteroid();
    applyNeurosteroid($person, $dangerous_chemical);

    if( isSmart($person,(object)['intelligence'=>110]) ) 
        return $person;
    else 
        lobotomize($person);

    return $person;
}

Thus providing an ideal algorithm for the world's educational problems. Aaand I'll stop there before I tweak this into something else..

0

No regex overhead - should be reasonably efficient, strlen just counts the bytes

Drop the array_values() if you don't care about indexes

Make it into function explode_interesting( $array, $fix_index = 0 ) if you want

$interesting = array_values( 
                 array_filter(
                   explode('/', '/1//2//3///4/0/false' ),
                   function ($val) { return strlen($val); }
               ));

echo "<pre>", var_export( $interesting, true ), "</pre>";

enjoy, Jeff

Jeff
  • 1
0

Write a wrapper function to strip them

function MyExplode($sep, $str)
{
    $arr = explode($sep, $str);
    foreach($arr as $item)
        if(item != "")
            $out[] = $item;
    return $out;
}
Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
0

Use this function to filter the output of the explode function

  function filter_empty(&$arrayvar) {
        $newarray = array();
        foreach ($arrayvar as $k => $value)
            if ($value !== "")
                $newarray[$k] = $value;

        $arrayvar = $newarray;
    }
AntonioCS
  • 8,335
  • 18
  • 63
  • 92
  • If you're taking in the array as a reference, couldn't you then just unset() the empty-string indexes, without the need for an additional temporary array? – Bobby Jack Sep 15 '08 at 17:09
0

Regular expression solutions tend to be much slower than basic text replacement, so i'd replace double seperators with single seperators, trim the string of any whitespace and then use explode:

// assuming $source = '1/2//3/';
$source = str_replace('//', '/', $source);
$source = trim($source);
$parts = explode('/', $source);
Adam Hopkinson
  • 28,281
  • 7
  • 65
  • 99
-1

I usually wrap it in a call to array_filter, e.g.

var_dump(array_filter(explode('/', '1/2//3/'))
=>
array(3) {
  [0]=>
  string(1) "1"
  [1]=>
  string(1) "2"
  [3]=>
  string(1) "3"
}

Be aware, of course, that array keys are maintained; if you don't want this behaviour, remember to add an outer wrapper call to array_values().

Bobby Jack
  • 15,689
  • 15
  • 65
  • 97
-1

PHP's split function is similar to the explode function, except that it allows you to enter a regex pattern as the delimiter. Something to the effect of:

$exploded_arr = split('/\/+/', '1/2//3/');
Bullines
  • 5,626
  • 6
  • 53
  • 93