John Beasley
John Beasley

Reputation: 3073

How to explode on every second comma-space in a string?

I found an error in some old code that hopefully I can fix pretty quickly, with the help of the SO community.

A user may insert multiple city/states into an array, so the array will look something like this:

 Norfolk, VA, Chicago, IL, New York, NY

I need to retain the inner comma and using the trailing comma as the delimiter.

The current PHP code gets the data like this:

 $deliveryCityArray = explode(',', $_POST['deliveryCity']);

When displayed in a grid, the City/State should be in the same column.

Unfortunately with my current code, the City will be in its own cell, and the State will be in the next row.

What do I need to add to retain the trailing comma but keep the inner comma?

Upvotes: 1

Views: 1607

Answers (4)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76413

There is a pattern in the string you're trying to process: You want to explode the input on each comma after 2 upper-case letters. In that case:

$array = preg_split('/(?<=, [A-Z]{2}),/', $str);

works just fine (DEMO)

How it works:

  • (?<=: Positive lookbehind assertion. The pattern will only match if it is preceded by a certain sub-pattern
  • , [A-Z]{2}): The sub-pattern: a comma, space and 2 upper-case chars (the closing bracket signals the end of the lookbehind assertion)
  • ,: a litteral , (which is matched if the lookbehind is successful). This character is matched and used to explode the string. The lookbehind is a zero-width match, so it's not treated as part of the delimiter

If the entire string is upper-case, the pattern needs to change a bit, but not a lot (Updated demo):

$array = preg_split('/(?<=,\s[A-Z]{2}\b),/', $upperStr);

I've simply added a \b to the lookbehind (metacharacter that matches a word-boundary). That word boundary is optional anyway, seeing as we're matching , [A-Z]{2}, anyway.

Upvotes: 2

mickmackusa
mickmackusa

Reputation: 47992

When using regex, for best efficiency avoid using capture groups and lookarounds. This task can be accomplished without these techniques by leveraging \K to "restart the fullstring match".

Code: (Demo)

$string='Norfolk, VA, Chicago, IL, New York, NY';
var_export(preg_split('/[A-Z]{2}\K, /',$string));

Output:

array (
  0 => 'Norfolk, VA',
  1 => 'Chicago, IL',
  2 => 'New York, NY',
)

This pattern effectively says:

  • Look for the abbreviated states (two uppcase letters) then
  • Restart the fullstring match then
  • Use the comma and space as the delimiter. Simple and Clean.

Upvotes: 0

Liam Wiltshire
Liam Wiltshire

Reputation: 1264

The only way you can do this is to make some assumptions - as long as you are confident that the 'separating' comma is always every other one, you could do something like this:

$results = array();
$tmp = explode(",",$_POST['deliveryCity']);
$idx = 0;
while ($idx < count($tmp)){
   $results[] = $tmp[$idx] . "," . $tmp[$idx+1];
   $idx += 2;
}

//$results will contain your resultant array.

Upvotes: 2

Mark Baker
Mark Baker

Reputation: 212452

Explode as normal, then chunk that resultant array into pairs, then walk the pairs and implode them again

$deliveryCity = 'Norfolk, VA, Chicago, IL, New York, NY';

$deliveryCityArray = array_map(
    function($value) {
        return implode(',', $value);
    },
    array_chunk(
        explode(',', $deliveryCity),
        2
    )
);

Demo

Upvotes: 3

Related Questions