Foreach с передачей по ссылке дает другой результат, чем передача по значению - PullRequest
1 голос
/ 10 ноября 2011

Я написал несколько строк кода в PHP, чтобы переименовать повторяющиеся значения в массиве (с некоторым вдохновением отсюда ). По сути, я просматриваю исходный массив ($ headers []), используя ключи другого массива ($ header_test []) для отслеживания дубликатов. Если есть дубликат, я изменяю значение этого элемента в $ headers [].

Но странная вещь заключается в том, что я не получал правильных результатов, передавая по ссылке в foreach. Я должен был установить значения, используя полный формат $ array_name [$ key] = $ new_value. Почему это так?

(Оповещение о спойлере: ответ VolkerK верный - необходимо сбросить заголовок $ (переменная foreach "$ value"), и тогда он заработает.)

Здесь:

Используя этот вход:

$headers = array('Abc 123 ghi',
            'dangdarn',
            'oops32',
            'poss dup',
            'poss dup',
            'pos  _s_ dup',
            'bad chars\'& 3% 9'
           );

Затем применение этой пользовательской функции, которая, я думаю, не повлияет на проблему:

    function mysql_clean_string($string) {
    //remove non alnum characters
    $string =  preg_replace("/[^a-zA-Z0-9\s]/", "_", $string);

    // replace 1+ spaces or 1+ '_' with a single '_'
    $string =  preg_replace("/[ _]+/", "_", $string);

    $string = trim($string,'_');

    if(strlen($string) > 20) {
        $string = substr_replace($string,'',strpos($string,'_',20));
    }

    $string = strtolower($string);

    return $string;
}

array_walk($headers,'mysql_clean_string'); // limit headers to alnum chars

Передача значений массива по ссылке не работает должным образом (см. Ниже для var_dump ()):

    $header_test = array();
    foreach($headers as &$header) { //passing by reference
        $temp = $header;
        if(array_key_exists($temp,$header_test)) {

            **$header = $header . '_' . $header_test[$header];**
            $header_test[$temp]++; 
            unset($temp);
        } else {
            $header_test[$temp] = 1;
        }
    }

//here is the solution VolkerK suggested and it works:
unset($header);

Вот выходные данные var_dump с неверными результатами (дублирующиеся значения и уведомление «&» в заголовках [6] и пропущенное последнее значение):

  ["headers"]=>
  array(7) {
    [0]=>
    string(11) "abc_123_ghi"
    [1]=>
    string(8) "dangdarn"
    [2]=>
    string(6) "oops32"
    [3]=>
    **string(8) "poss_dup"**
    [4]=>
    string(10) "poss_dup_1"
    [5]=>
    string(9) "pos_s_dup"
    [6]=>
    **&string(8) "poss_dup"**
  }
  ["header_test"]=>
  array(6) {
    ["abc_123_ghi"]=>
    int(1)
    ["dangdarn"]=>
    int(1)
    ["oops32"]=>
    int(1)
    ["poss_dup"]=>
    int(2)
    ["pos_s_dup"]=>
    int(1)
    ["bad_chars_3_9"]=>
    int(1)
  }

А теперь вот что работает, это использовать $ original_array [$ key] = $ new_value формат:

$header_test = array();
foreach($headers as $key => $header) {
    $temp = $header;
    if(array_key_exists($temp,$header_test)) {

        **$headers[$key] = $header . '_' . $header_test[$header];**
        $header_test[$temp]++; 
        unset($temp);
    } else {
        $header_test[$temp] = 1;
    }
}

var_dump:

  ["headers"]=>
  array(7) {
    [0]=>
    string(11) "abc_123_ghi"
    [1]=>
    string(8) "dangdarn"
    [2]=>
    string(6) "oops32"
    **[3]=>
    string(8) "poss_dup"
    [4]=>
    string(10) "poss_dup_1"**
    [5]=>
    string(9) "pos_s_dup"
    **[6]=>
    string(13) "bad_chars_3_9"**
  }
  ["header_test"]=>
  array(6) {
    ["abc_123_ghi"]=>
    int(1)
    ["dangdarn"]=>
    int(1)
    ["oops32"]=>
    int(1)
    ["poss_dup"]=>
    int(2)
    ["pos_s_dup"]=>
    int(1)
    ["bad_chars_3_9"]=>
    int(1)
  }

VolkerK предложил решение. Помимо его других хороших предложений, ключевым моментом было удаление заголовка $ после цикла foreach.

Ответы [ 2 ]

1 голос
/ 10 ноября 2011

просто принимая ваш код

<?php
$headers = array('Abc 123 ghi',
            'dangdarn',
            'oops32',
            'poss dup',
            'poss dup',
            'pos  _s_ dup',
            'bad chars\'& 3% 9'
           );

$header_test = array();
foreach($headers as &$header) { //passing by reference
    $temp = $header;
    if(array_key_exists($temp,$header_test)) {

        $header = $header . '_' . $header_test[$header];
        $header_test[$temp]++; 
        unset($temp);
    } else {
        $header_test[$temp] = 1;
    }
}

var_dump($headers);

производит

array(7) {
  [0]=>
  string(11) "Abc 123 ghi"
  [1]=>
  string(8) "dangdarn"
  [2]=>
  string(6) "oops32"
  [3]=>
  string(8) "poss dup"
  [4]=>
  string(10) "poss dup_1"
  [5]=>
  string(12) "pos  _s_ dup"
  [6]=>
  &string(16) "bad chars'& 3% 9"
}

на моей машине с использованием php 5.3.5 / win32. Похоже, ваш "настоящий" код делает что-то еще с $ header в / и после цикла.

Немного упрощено (исключая $ tmp):

<?php
$headers = getData();
$header_test = array();
foreach($headers as &$header) { //passing by reference
    if( array_key_exists($header, $header_test) ) {
        $header = $header . '_' . $header_test[$header]++;
    }
    else {
        $header_test[$header] = 1;
    }
}

// removes the reference that's causing the & in front
// of the last element of $headers in the output of var_dump
// if $headers can be empty you need to guard this
// to avoid "undefined variable 'header' warning.
// Probably better to put this code in a function
// so that $header can fall out of scope automagically
unset($header); 

var_dump($headers);

function getData() {
    return array('Abc 123 ghi',
        'dangdarn',
        'oops32',
        'poss dup',
        'poss dup',
        'pos  _s_ dup',
        'bad chars\'& 3% 9',
        'poss dup',
        'poss dup'
    );
}

печать

array(9) {
  [0]=>
  string(11) "Abc 123 ghi"
  [1]=>
  string(8) "dangdarn"
  [2]=>
  string(6) "oops32"
  [3]=>
  string(8) "poss dup"
  [4]=>
  string(10) "poss dup_1"
  [5]=>
  string(12) "pos  _s_ dup"
  [6]=>
  string(16) "bad chars'& 3% 9"
  [7]=>
  string(10) "poss dup_2"
  [8]=>
  string(10) "poss dup_3"
}
0 голосов
/ 10 ноября 2011

Это должно быть

foreach(&$headers as $header) { //passing by reference

. Заголовки - это ссылка на массив, а $ header - дескриптор для каждого члена.Вы можете заменить дескриптор любым идентификатором.

...