Bir RecursiveArrayIterator kullanırken nasıl dizi anahtarlarını ve değerlerini değiştirmek için ne yapmalıyım?

5 Cevap php

Ben burada aptal bir şey yapıyorum sanıyorum, ama ben SPL ile basit bir sorun gibi görünüyor ne kafamı karıştırdı:

Nasıl kullanarak, bir dizi (bu örnekte değerleri) içeriğini modifiye mı a RecursiveArrayIterator / RecursiveIteratorIterator?

Takip test kodu kullanarak, ben getInnerIterator() ve offsetSet() kullanılarak döngü içinde değerini değiştirmez, ve ben döngü içinde olduğum sürece modifiye dizi atabilirsin.

Ben döngü bırakın ve yineleyici gelen dizi dökümü Ama, ne zaman geri orijinal değerlerine bulunuyor. Ne oluyor?

$aNestedArray = array();
$aNestedArray[101] = range(100, 1000, 100);
$aNestedArray[201] = range(300, 25, -25);
$aNestedArray[301] = range(500, 0, -50);

$cArray = new ArrayObject($aNestedArray);
$cRecursiveIter = new RecursiveIteratorIterator(new RecursiveArrayIterator($cArray), RecursiveIteratorIterator::LEAVES_ONLY);

// Zero any array elements under 200  
while ($cRecursiveIter->valid())
{
    if ($cRecursiveIter->current() < 200)
    {
        $cInnerIter = $cRecursiveIter->getInnerIterator();
        // $cInnerIter is a RecursiveArrayIterator
        $cInnerIter->offsetSet($cInnerIter->key(), 0);
    }

    // This returns the modified array as expected, with elements progressively being zeroed
    print_r($cRecursiveIter->getArrayCopy());

    $cRecursiveIter->next();
}

$aNestedArray = $cRecursiveIter->getArrayCopy();

// But this returns the original array.  Eh??
print_r($aNestedArray);

5 Cevap

Onlar ArrayIterator (RecursiveArrayIterator onun offset*() yöntemleri devralır ve kurucusuna atfen tarafından kabul edilemez, çünkü düz dizilerde değerler değiştirilebilir değil gibi görünüyor bu sınıf, bakın SPL Reference). Yani tüm aramalar için offsetSet() dizisinin bir kopyası üzerinde çalışır.

Ben onlar (varsayılan durumda olmalıdır ArrayObject örneklerini geçerken yani) bir nesne yönelimli bir ortamda çok mantıklı değil çünkü Call-by-başvuru önlemek için seçtim sanırım.

Bu göstermek için biraz daha kod:

$a = array();

// Values inside of ArrayObject instances will be changed correctly, values
// inside of plain arrays won't
$a[] = array(new ArrayObject(range(100, 200, 100)),
             new ArrayObject(range(200, 100, -100)),
             range(100, 200, 100));
$a[] = new ArrayObject(range(225, 75, -75));

// The array has to be
//     - converted to an ArrayObject or
//     - returned via $it->getArrayCopy()
// in order for this field to get handled properly
$a[] = 199;

// These values won't be modified in any case
$a[] = range(100, 200, 50);

// Comment this line for testing
$a = new ArrayObject($a);

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));

foreach ($it as $k => $v) {
    // getDepth() returns the current iterator nesting level
    echo $it->getDepth() . ': ' . $it->current();

    if ($v < 200) {
        echo "\ttrue";

        // This line is equal to:
        //     $it->getSubIterator($it->getDepth())->offsetSet($k, 0);
        $it->getInnerIterator()->offsetSet($k, 0);
    }

    echo ($it->current() == 0) ? "\tchanged" : '';
    echo "\n";
}

// In this context, there's no real point in using getArrayCopy() as it only
// copies the topmost nesting level. It should be more obvious to work with $a
// itself
print_r($a);
//print_r($it->getArrayCopy());

GetInnerIterator bir copy alt yineleyici yaratır gibi görünüyor.

Belki farklı bir yöntem var mıdır? (Bizi izlemeye devam edin ..)


Güncelle: PHP size subIterator değerlerini değiştirmek için bir yol verir gibi bir süre için hack, ve 3 diğer mühendisler çekerek sonra, o görünmüyor.

Her zaman eski standı ile kullanabilirsiniz:

<?php  
// Easy to read, if you don't mind references (and runs 3x slower in my tests) 
foreach($aNestedArray as &$subArray) {
    foreach($subArray as &$val) {
       if ($val < 200) {
            $val = 0;
        }
    }
}
?>

VEYA

<?php 
// Harder to read, but avoids references and is faster.
$outherKeys = array_keys($aNestedArray);
foreach($outherKeys as $outerKey) {
    $innerKeys = array_keys($aNestedArray[$outerKey]);
    foreach($innerKeys as $innerKey) {
        if ($aNestedArray[$outerKey][$innerKey] < 200) {
            $aNestedArray[$outerKey][$innerKey] = 0;
        }
    }
}
?>

Ben bu doğrudan cevap vermez biliyorum, ama bunun üzerinde yineleme sırasında yineleme altında nesneyi değiştirmek için iyi bir uygulama değil.

Bu değere göre iletme vs referans geçirerek aşağı gelebilir misiniz?

Örneğin değiştirmeyi deneyin:

$cArray = new ArrayObject($aNestedArray);

için:

$cArray = new ArrayObject(&$aNestedArray);

Iterator sınıfları kullanarak değil (RecursiveArrayIterator::beginChildren() yerine referans geçen on copying veri gibi görünüyor.)

Siz ne istediğinizi elde etmek için aşağıdakileri kullanabilirsiniz

function drop_200(&$v) { if($v < 200) { $v = 0; } }

$aNestedArray = array();
$aNestedArray[101] = range(100, 1000, 100);
$aNestedArray[201] = range(300, 25, -25);
$aNestedArray[301] = range(500, 0, -50);

array_walk_recursive ($aNestedArray, 'drop_200');

print_r($aNestedArray);

veya kullanım create_function() yerine drop_200 işlevinin oluşturulması, ama sizin kilometre create_function ve bellek kullanımı ile değişebilir.