Explode string into array with no empty elements?
Asked Answered
S

12

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"
}
Shopping answered 15/9, 2008 at 16:45 Comment(0)
U
71

Try preg_split.

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

Ultravirus answered 15/9, 2008 at 16:48 Comment(14)
I think this is the most correct because it maintains the correct array indexes.Shopping
@Ryono: what do you mean by that, exactly?Masonmasonic
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.Shopping
@RafikBari The first character of a regex pattern is its delimiter. See php.net/manual/en/regexp.reference.delimiters.php. / is typically used, but I used @ here as / was in our string.Ultravirus
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.Glennieglennis
@ceejay it would be better if the explanation of your snippet existed in the answer body instead of in the comments under the answer.Vernon
@simsek calling array_filter() will also destroy 0 elements in the output -- a side effect that this answer doesn't suffer.Vernon
@Vernon You have enough rep to propose an edit, if you desire.Ultravirus
I wouldn't want to put words in your mouth. Your answer deserves your own personal explanation.Vernon
@Vernon Feel free. That's why SO has an edit button.Ultravirus
You are the one harvesting unicorn points from this code + link answer. Surely you can spare the time to role model better posting behaviors.Vernon
I am comfortable with the state of the thirteen year old answer as it stands.Ultravirus
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.Vernon
@Ultravirus 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.Sastruga
C
33

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

Citystate answered 15/9, 2008 at 16:49 Comment(3)
This will remove '0', too, as it is false in a boolean context. So it might not be what you want!Gavette
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."Citystate
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.Stier
S
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.

Shopping answered 15/9, 2008 at 17:14 Comment(1)
There's no need for a callback function when you use array_filter if you're filtering just falsy values like empty stringRusert
T
6
function not_empty_string($s) {
  return $s !== "";
}

array_filter(explode('/', '1/2//3/'), 'not_empty_string');
Toiletry answered 15/9, 2008 at 16:48 Comment(2)
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.Masonmasonic
Good comment. I Wanted to suggested the 'bare' call, but yes, it will throw out the zero.Siler
P
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"
}
Porty answered 2/5, 2014 at 1:57 Comment(0)
C
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..

Counterinsurgency answered 10/3, 2016 at 22:12 Comment(0)
D
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;
}
Dimitry answered 15/9, 2008 at 16:50 Comment(0)
C
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;
    }
Converge answered 15/9, 2008 at 17:2 Comment(1)
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?Masonmasonic
S
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);
Syracuse answered 16/9, 2008 at 14:26 Comment(1)
But what if it's 1/2////3/? Your solution only makes one pass and will lease two consecutive slashes.Vernon
R
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

Rammer answered 24/3, 2016 at 3:51 Comment(1)
array_filter() will happily receive strlen as a callback string.Vernon
M
-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().

Masonmasonic answered 15/9, 2008 at 16:51 Comment(4)
Array filter filtesrs by FALSE by default. You need a callback function to make it workDimitry
Uh, Fire Lancer, read the docs. "Example #2 array_filter() without callback"Ultravirus
Yeah, it depends what you call "work" - see my comment on James Aylett's post. Ironically, your version will also remove occurrences of "0" in the original string.Masonmasonic
oh ok...I forgot php considered empty strings as FALSe, though it was just 0 and nullDimitry
B
-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/');
Bates answered 15/9, 2008 at 16:54 Comment(2)
This function has been DEPRECATED as of PHP 5.3.0. Relying on this feature is highly discouraged.Genesia
Use preg_split insteadDudgeon

© 2022 - 2024 — McMap. All rights reserved.