23

Example:

$string = "This is some text written on 2010-07-18.";
preg_match('|(?<date>\d\d\d\d-\d\d-\d\d)|i', $string, $arr_result);
print_r($arr_result);

Returns:

Array
(
    [0] => 2010-07-18
    [date] => 2010-07-18
    [1] => 2010-07-18
)

But I want it to be:

Array
(
    [date] => 2010-07-18
)

In PHP's PDO object there is an option that is filtering results from database by removing these duplicate numbered values : PDO::FETCH_ASSOC. But I haven't seen similar modifier for the PCRE functions in PHP yet.

HamZa
  • 14,671
  • 11
  • 54
  • 75
rsk82
  • 28,217
  • 50
  • 150
  • 240

9 Answers9

20

How to return only named groups with preg_match or preg_match_all?

This is currently (PHP7) not possible. You will always get a mixed type array, containing numeric and named keys.

Lets quote the PHP manual (http://php.net/manual/en/regexp.reference.subpatterns.php):

This subpattern will then be indexed in the matches array by its normal numeric position and also by name.


To solve the problem the following code snippets might help:

1. filter the array by using an is_string check on the array key (for PHP5.6+)

$array_filtered = array_filter($array, "is_string", ARRAY_FILTER_USE_KEY);

2. foreach over the elements and unset if array key is_int() (all PHP versions)

/**
 * @param array $array
 * @return array
 */
function dropNumericKeys(array $array)
{
    foreach ($array as $key => $value) {
        if (is_int($key)) {
            unset($array[$key]);
        }
    }
    return $array;
}

Its a simple PHP function named dropNumericKeys(). Its for the post-processing of an matches array after a preg_match*() run using named groups for matching. The functions accepts an $array. It iterates the array and removes/unsets all keys with integer type, leaving keys with string type untouched. Finally, the function returns the array with "now" only named keys.

Note: The function is for PHP downward compatiblity. It works on all versions. The array_filter solution relies on the constant ARRAY_FILTER_USE_KEY, which is only available on PHP5.6+. See http://php.net/manual/de/array.constants.php#constant.array-filter-use-key

Jens A. Koch
  • 39,862
  • 13
  • 113
  • 141
  • why define a function if you have a solution as simple as #1 – Meisner Dec 25 '16 at 13:46
  • 2
    The function is for PHP downward compatiblity. It works on all versions. The `array_filter` solution relies on the constant `ARRAY_FILTER_USE_KEY`, which is only available on PHP5.6+. See http://php.net/manual/de/array.constants.php#constant.array-filter-use-key – Jens A. Koch Dec 25 '16 at 15:20
  • 3rd option... use `array_intersect_key`... with a list of the names – Brad Kent Feb 07 '22 at 14:57
14

preg_match does not have any flag or option that it only returns named matches (yet). So what you want is not directly possible. However you can remove all items with non-fitting keys from your matches array and then you get what you're looking for:

$matches = array_intersect_key($matches, array_flip(array('name', 'likes')));
hakre
  • 193,403
  • 52
  • 435
  • 836
4

I do not think you can make preg_* do it, but you can do it with a simple loop. But I don't see why those elements pose a problem.

HamZa
  • 14,671
  • 11
  • 54
  • 75
Maerlyn
  • 33,687
  • 18
  • 94
  • 85
  • In small arrays this not pose a problem but in very big with lots of patterns there might be problems with high memory needs. Second, it looks cleaner that way, and there would be (with this non existent modifier) one line of code less, so less possibility of an error. – rsk82 Jul 18 '10 at 14:59
  • Quite the opposite: because this can't be done by PCRE, you'd have to add more code, where there could be more problems. – Charles Jul 18 '10 at 16:28
  • I meant the additional elements in the array. – Maerlyn Jul 18 '10 at 16:39
  • What the opposite, if this modifier i ask for would exist then that extra line wouldn't be needed, it would be even better if it would be a global modifier that would set preferences of all pcre functions setting mode for output arrays I repeat it: it was done in PDO object, what it can't be done in pcre ? – rsk82 Jul 19 '10 at 09:14
  • as you said: PDO object-based, pcre is still function-based. They'll possibly change it some time later, as the language evolves. – Maerlyn Jul 19 '10 at 10:53
  • You could also use [T-Regx](https://t-regx.github.io) library which has `namedGroups()` method :D – Danon Nov 12 '18 at 15:17
3

Similar to the answer that hakre posted above, I use this snippet to get just the named parameters:

$subject = "This is some text written on 2010-07-18.";
$pattern = '|(?<date>\d\d\d\d-\d\d-\d\d)|i';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
echo '<pre>Before Diff: ', print_r($matches, 1), '</pre>';
$matches = array_diff_key($matches[0], range(0, count($matches[0])));
echo '<pre>After Diff: ', print_r($matches, 1), '</pre>';

...which produces this:

Before Array
(
    [0] => Array
        (
            [0] => 2010-07-18
            [date] => 2010-07-18
            [1] => 2010-07-18
        )

)
After Array
(
    [date] => 2010-07-18
)
Community
  • 1
  • 1
2

It also possible to unset all numeric indexes before return:

foreach (range(0, floor(count($arr_result) / 2)) as $index) {
    unset($arr_result[$index]);
}
Vitaly Chirkov
  • 1,692
  • 3
  • 17
  • 33
1

I read in your post that these are possible overloads of future memory etc ... In this case, why no't can be solved with an unset():

$string = "This is some text written on 2010-07-18.";
preg_match('|(?<date>\d{4}-\d{2}-\d{2})|i', $string, $arr_result);
$date = array("date" => $arr_result['date']);
unset($arr_result, $string);//delete array and string preg_match origen

print_r($date);
//or create a new:
//  $arr_result = $date;
//print_r($arr_result);
Gugo
  • 11
  • 3
1

You could use T-Regx and go with group() or namedGroups() which only returns named capturing groups.

<?php
$subject = "This is some text written on 2010-07-18.";

pattern('(?<date>\d\d\d\d-\d\d-\d\d)', 'i')->match($subject)->first(function ($match) {

    $date = $match->get('date'); 
    // 2010-07-18

    $groups = $match->namedGroups(); 
    // [
    //   'date' => '2010-07-18'
    // ]   
});
Danon
  • 2,771
  • 27
  • 37
0

I use some of introduced codes and this is the final code works on php 5.6+:

$re = '/\d+\r\n(?<start>[\d\0:]+),\d+\s--\>\s(?<end>[\d\0:]+),.*\r\nHOME.*\r\nGPS\((?<x>[\d\.]+),(?<y>[\d\.]+),(?<d>[\d\.]+)\)\sBAROMETER\:(?<h>[\d\.]+)/';

$str= file_get_contents($srtFile);
preg_match_all($re, $str, $matches, PREG_SET_ORDER, 0);
echo '<pre>';
$filtered=array_map(function ($d){
     return $array_filtered = array_filter($d, "is_string", ARRAY_FILTER_USE_KEY);
    },$matches);
var_dump($filtered);

if you are interested what it does it read position data from a str file that DJI drones generate while recording video.

MSS
  • 3,520
  • 24
  • 29
-2

Try this:

$string = "This is some text written on 2010-07-18."; 
preg_match('|(?<date>\d\d\d\d-\d\d-\d\d)|i',$string,$arr_result);
echo $arr_result['date'];
takrl
  • 6,356
  • 3
  • 60
  • 69
  • no, that is wrong, I meant a modifier to automatically delete keys that are numbers, it's not that easy on multibranched array that preg_match_all might produce – rsk82 Jul 19 '10 at 09:11