我有一组具有id
和parentId
属性的对象。 id
值是唯一的,但多个对象可能具有相同的parentId
值。
如果多个对象具有相同的parentId
值,我想删除除一个之外的所有对象(即删除"兄弟姐妹")。
我认为我可以使用嵌套的foreach
循环轻松完成此操作,但它无法正常工作。
以下是一个例子:
$objArray = [];
for($i = 0; $i < 2; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 1;
$objArray[] = $obj;
}
for($i = 2; $i < 4; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 2;
$objArray[] = $obj;
}
echo 'Before unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';
// loop over $objArray and remove elements with the same ->parentId (leaving one)
foreach ($objArray as $keyOuter => $objOuter) {
foreach ($objArray as $keyInner => $objInner) {
if ($objInner->id != $objOuter->id // if the inner object is NOT the same as the outer object (i.e. it's a different object)
&& $objInner->parentId == $objOuter->parentId // and if the parent IDs are the same
) {
unset($objArray[$keyInner]); // unset the inner object
}
}
}
echo 'After unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';
输出:
Before unsetting siblings:
Array
(
[0] => stdClass Object
(
[id] => 0
[parentId] => 1
)
[1] => stdClass Object
(
[id] => 1
[parentId] => 1
)
[2] => stdClass Object
(
[id] => 2
[parentId] => 2
)
[3] => stdClass Object
(
[id] => 3
[parentId] => 2
)
)
After unsetting siblings:
Array
(
)
我希望数组中的第一个和第三个对象保留在foreach
循环之后,但正如您所看到的那样,数组中的所有对象都将被删除。
我在这里缺少什么?
答案 0 :(得分:1)
for循环的前两行是相同的,使用var_dump来确保自己:
foreach ($objArray as $keyOuter => $objOuter) {
foreach ($objArray as $keyInner => $objInner) {
var_dump($objArray, $objInner); //the same object is returned
...
所以从技术上讲,objOuter总是在第二个foreach的第一次运行中等于objInner,因此删除对象,最后你将删除所有对象。
为这段代码赋予变量一个不同的名称:
for($i = 2; $i < 4; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 2;
$objArray2[] = $obj; //instead of $objArray
}
这是最终的代码,它按您希望的方式工作:
for($i = 0; $i < 2; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 1;
$objArray[] = $obj;
}
for($i = 2; $i < 4; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 2;
$objArray2[] = $obj;
}
echo 'Before unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';
foreach ($objArray as $keyOuter => $objOuter) {
foreach ($objArray2 as $keyInner => $objInner) {
if ($objInner->id != $objOuter->id
&& $objInner->parentId == $objOuter->parentId) {
unset($objArray[$keyInner]); // unset the inner object
}
}
}
echo 'After unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';
答案 1 :(得分:1)
我在你的内循环中添加了这一行:
echo 'Matched id #'.$objOuter->id.' parent #'.$objOuter->parentId.' with id #'.$objInner->id.' parent #'.$objInner->parentId."\r\n";
产生了:
匹配的id#0父#1,id#1 parent#1
匹配id#1 parent#1,id#0 parent#1
匹配id#2 parent#2,id#3 parent#2
匹配id#3父#2,id#2 parent#2
另一个演示(!!
表示没有匹配/删除,==
表示父匹配删除):
0/1!!0/1 0/1==1/1 0/1!!2/2 0/1!!3/2
1/1==0/1 1/1!!2/2 1/1!!3/2
2/2!!2/2 2/2==3/2
3/2==2/2
看模式?在您拥有$objArray
已匹配的元素后,您的内循环获取最新版本的unset
,而您的外部循环没有新版本,因为foreach
实际上保留了$objOuter
和$keyOuter
值的临时克隆数组。
这是一个概念证明:
$array = array(1,2,3,4);
foreach ($array as $a) {
if (isset($array)) unset($array);
echo $a;
}
输出:
1234
或者这个:
$array = array(1,2,3,4);
foreach ($array as $a) {
$array[3] = 100;
echo $a; // Output is still 1234
}
或者这个:
$array = array(1,2,3,4);
foreach ($array as $a) {
if (isset($array[3])) unset($array[3]);
echo $a; // Yet again 1234
}
如果$array
不再存在,为什么循环仍然继续?按照同样的逻辑,为什么不是我的第二个例子123100
的输出?你的外环存在同样的缺陷/错误。
我宁愿创建一个新的过滤数组,而不是尝试使用嵌套循环从原始数据中删除:
$newArray = array();
foreach ($objArray as $obj) {
if (!isset($newArray[$obj->parentId])) { // Use the index to test for existing parent IDs
$newArray[$obj->parentId] = $obj;
}
}
// Optional - use array_values to get rid of parentId in the indices
$newArray = array_values($newArray);
// or you can just do this if you want to replace $objArray
$objArray = array_values($newArray);
unset($newArray);
如果密钥已存在,您还可以保留另一个现有父密钥数组,然后从现有数组中删除:
$existingParents = array();
foreach ($objArray as $key => $obj) {
if (isset($existingParents[$obj->parentId])) {
unset($objArray[$key]);
} else {
$existingParents[$obj->parentId] = true;
}
}
答案 2 :(得分:1)
快速解决方案:
$objArray = [];
for($i = 0; $i < 2; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 1;
$objArray[] = $obj;
}
for($i = 2; $i < 4; $i++) {
$obj = new stdClass();
$obj->id = $i;
$obj->parentId = 2;
$objArray[] = $obj;
}
echo 'Before unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';
// loop over $objArray and remove elements with the same ->parentId (leaving one)
$max = count($objArray);
for ($i=0; $i<$max; $i++) {
$objOuter = $objArray[$i];
foreach ($objArray as $i2=>$objInner) {
if ($objInner->id != $objOuter->id // if the inner object is NOT the same as the outer object (i.e. it's a different object)
&& $objInner->parentId == $objOuter->parentId // and if the parent IDs are the same
) {
unset($objArray[$i2]);
$max=$max-1;// unset the inner object
}
}
}
echo 'After unsetting siblings:<pre>';
print_r($objArray);
echo '</pre>';