rsk82
rsk82

Reputation: 29407

How to return only named groups with preg_match or preg_match_all?

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.

Upvotes: 24

Views: 21044

Answers (9)

Danon
Danon

Reputation: 2983

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'
    // ]   
});

Upvotes: 1

hakre
hakre

Reputation: 198209

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')));

Upvotes: 14

MSS
MSS

Reputation: 3860

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.

Upvotes: 0

Gugo
Gugo

Reputation: 11

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);

Upvotes: 1

Jens A. Koch
Jens A. Koch

Reputation: 41796

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

Upvotes: 22

Daphne Dorman
Daphne Dorman

Reputation: 31

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
)

Upvotes: 3

Maerlyn
Maerlyn

Reputation: 34125

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.

Upvotes: 4

Vitaly Chirkov
Vitaly Chirkov

Reputation: 1722

It also possible to unset all numeric indexes before return:

foreach (range(0, floor(count($arr_result) / 2)) as $index) {
    unset($arr_result[$index]);
}

Upvotes: 2

Rahul A
Rahul A

Reputation: 1

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'];

Upvotes: -3

Related Questions