2013-04-18 3 views
0

В настоящее время я работаю над классом создания общей формы и вчера имел проблему. Я сделал фрагмент, чтобы воспроизвести проблему.Удаление элементов из массива в пределах foreach

По существу, я хочу удалить элементы, которые сгруппированы из массива исходных элементов после того, как вся группа была нарисована, и я делаю это, перебирая массив элементов.

Фрагмент кода должен охватывать проблему, я что-то пропустил здесь? По моим сведениям, удаление элемента в то время как foreach полностью безопасно и законно, поскольку foreach внутренне использует только копию, которая может быть изменена во время цикла.

$ids = array('a' => array(), 'b' => array(), 'c' => array()); 
$groups['g1'] = array('a', 'c'); 
foreach($ids as $id => $element) { 

    //var_dump($ids); 
    $g_id = ''; 

    // search the id in all groups 
    foreach($groups as $group_id => $group) { 
     if(in_array($id, $group)) { 
      $g_id = $group_id; 
      break; 
     } 
    } 

    // element is part of a group 
    if($g_id !== '') { 

     //echo $g_id; 

     // element a and c gets unset within loop and should not be in $ids anymore 
     foreach($groups[$g_id] as $field_id) { 
      unset($ids[$field_id]); 

      echo $field_id; 
     } 
     unset($groups[$g_id]); 
    } else { 
     if($id === 'a' || $id === 'c') 
      echo $id; 
    } 
} 

элемент «с» получает сбросить в пределах Foreach (группы ..) цикл, но потом снова выводятся в другом отделении. Также, когда я сначала var_dump ($ fields), я всегда получаю 'a', 'b' и 'c' внутри. Я использую PHP 5.4.7.

Заранее спасибо

EDIT: я сделал ошибку в примере кода, его в настоящее время обновляется. Разумеется, все комментарии об использовании неправильного индекса (это было бы 0,1 и т. Д.). Значения при использовании var_dump не установлены сейчас, но я все равно попадаю в else с 'c' один раз.

EDIT2: Im не сделано с исходным кодом, но после прочтения комментариев, которые я в настоящее время придумал следующее решение отправленного фрагмента кода выше:

$ids=array("a"=>array(),"b"=>array(),"c"=>array(),"d"=>array(),"e"=>array()); 
$groups=array(array("a"),array("c", "e")); 
array_walk($groups,function($v,$i)use(&$ids){ 

    $in_both = array_intersect(array_keys($ids),$v); 
    //var_dump($in_both); 
    foreach($in_both as $b) { 
     unset($ids[$b]); 
    } 
}); 
print_r($ids); 

или

$ids=array("a"=>array(),"b"=>array(),"c"=>array(),"d"=>array(),"e"=>array()); 
$groups=array(array("a"),array("c")); 
array_walk($ids,function($v,$i)use(&$ids, $groups){ 
    $in_both = array(); 
    foreach($groups as $g) { 
     if(in_array($i,$g)) { 
      $in_both = array_intersect(array_keys($ids),$g); 
     } 
    } 

    foreach($in_both as $b) { 
     unset($ids[$b]); 
    } 
}); 
print_r($ids); 

Использование foreach не работает для меня в этом случае, потому что мне нужно изменить массив $ ids, в то время как цикл выполняет итерацию по нему.

В самом самом базовом положении код что-то вроде этого:

$ids = array('a', 'b'); 

while(count($ids)) { 
    array_pop($ids); 
    echo 'pop'; 
} 

echo 'empty'; 

Allthough Еогеасп может изменить исходные значения из массива не изменит копию массива, используемого для итерации как NL-х уже заявлено. Благодаря Passerby за идею использования array_walk для этого.

EDIT3: Обновлен код, отрезанный еще раз. Второй отрезанный allthough ведет себя также неопределенным. Удаление элементов из массива при итерации по нему кажется плохой идеей.

+0

Посмотрите [здесь] (http://stackoverflow.com/questions/10057671/how-foreach-actually-works). Вместо этого я предлагаю использовать цикл 'for'. – BlitZ

+3

'$ ids [$ field_id]' в вашем примере не существует, потому что он будет интерпретировать '$ ids [" a "]', а не '$ ids [0]'. Кроме того, я не могу ответить на ваш вопрос, не могли бы вы сделать это немного более понятным, например: «У меня есть два массива, которые выглядят как этот [код массива], и я хотел бы удалить, если ... так что это может быть позже выглядите так [array code]. "? – Passerby

+0

@Passerby Отличное наблюдение +1. Но эта ошибка ничего не меняет в выходе. Если вы измените '$ ids = array ('a', 'b', 'c');' в '$ ids = array ('a' => 'a', 'b' => 'b', 'c '=>' c '); 'ничего не меняется. –

ответ

1

Крис, если я правильно понимаю, вы не ожидаете, 'C', которая должна быть выведена в ELSE филиал?

Но он должен быть выдан. Ваша логика такова:

  • вы делаете идентификаторы и начинаете с id 'a'.
  • затем вы очищаете идентификаторы a и c от идентификаторов и удаляете группу g1, содержащую 'a'. Во время этого шага будут удалены удаленные идентификаторы, являющиеся a и c. (Очищающий и с из идентификаторов не будет иметь никакого влияния на foreach($ids as $id) как Еогеасп будет продолжаться с нетронутой копией даже после того, как массив идентификаторов был очищен.)
  • тогда вы ID «Ь»: не найден ни в одном группа. (фактически, пока нет какой-либо группы)
  • поэтому для 'b' вы вводите ветку else. Но if() внутри ветви else предотвращает вывод
  • , тогда вы делаете id 'c', который также не встречается ни в одной группе, потому что у вас есть уже удаленная группа g1! Помните, нет групп?
  • поэтому для 'c' вы также вводите ветку else. И на этот раз if() внутри ветви else дает выход! Выход был только c

Таким образом, общий выход действительно соответствует.

Хорошо знать, что foreach(), который продолжается с нетронутой копией даже после того, как ее элементы были очищены, представляет собой специфическую вещь PHP. Другой язык не обязательно делает то же самое.

+0

Этот ответ, вероятно, именно моя проблема. Фактический код довольно сложный, чем мой образец, но я обязательно посмотрю на него и отчитаюсь. – Chris

0

Ваш Еогеасп обыкновение делать какие-либо изменения, так как копия массива используется .. Вам нужно будет использовать проход по ссылке для того, чтобы это работало. один из способов указан ниже

while(list($key,$value) = each($array)){ 
    if(your reason to unset) 
     unset($array[$key]); 
} 

это удалит элемент из массива.

+0

К сожалению, OP использует оригинал: 'unset ($ ids [$ field_id]);'. И это не _copy_. – BlitZ

+0

все равно будет работать – Dinesh

+0

Я просто хочу прояснить ситуацию. – BlitZ

1

$ids[$field_id] не существует, вы используете значение вместо ключа.

Вы должны просто снята с охраны с помощью правой клавиши:

if (in_array($field_id, $ids)) 
    unset($ids[array_search($field_id, $ids)]); 
+0

Это не меняет результат, так как я ответил на Passerby, а также –

+0

Это было протестировано, и оно работает ... Это не изменит ваш результат, так как вы просто echo '$ field_id' ... Вы должны попробовать' print_r ($ ids); 'в конце вашего скрипта. – soju

+0

Спасибо за ответ, я обновил свой пост, у меня была ошибка в моем примере кода, это была не проблема, но вы, конечно же, получили upvote :) – Chris

1

Я перепроверки в настоящее время. Но я думаю, что отключение массива с foreach - не действительно безопасно.

То, что я обычно делал, это взять забег и начать с самых высоких индексов и уменьшить индекс по пути. for($i = count($arr)-1; $i >= 0; $i--) { unset($array[$i]); }

Я отредактировал это сообщение через несколько минут.

Редактировать: Я был в замешательстве. Претендент с $ i ++ действительно является виновником. Еогеасп безопасен (в PHP не на всех языках!)

<?php 

$arr = Array(1,2,3,4,5,6,7,8,9,10); 
foreach ($arr as $key=>$val) 
    unset($arr[$key]); 
echo implode(',',$arr); // returns nothing 


$arr = Array(1,2,3,4,5,6,7,8,9,10); 
for ($i=0; $i<count($arr); $i++) 
    unset($arr[$i]); 
echo implode(',',$arr); // returns 6,7,8,9,10 


$arr = Array(1,2,3,4,5,6,7,8,9,10); 
for ($i=count($arr)-1; $i>=0; $i--) 
    unset($arr[$i]); 
echo implode(',',$arr); // returns nothing 

?> 
+0

Почему это не должно быть безопасно с foreach? – bestprogrammerintheworld

+0

@bestprogrammerintheworld, я должен был смутить. См. Мое редактирование –

+0

ok :-) просто любопытство - какой язык (ы) foreach небезопасен для использования (с такой функциональностью) – bestprogrammerintheworld

1

провел некоторое время на чтение кода, и я предполагаю ваша процедура:

  • Для каждого элемента в $ids, проверка если он существует в некоторой подматрице в $groups;
  • Если он существует, удалите все в $ids, которое также существует в этом подматрице.

Следуя приведенной выше логике, я придумал это:

$ids=array("a","b","c","d","e"); 
$groups=array(array("a","c"),array("c","e")); 
array_walk($groups,function($v,$i)use(&$ids){ 
    $ids=array_diff($ids,$v); 
}); 
print_r($ids);//debug 

Live demo

Смежные вопросы