[php] Array id resetten?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Martine
  • Registratie: Mei 2002
  • Niet online
Ik heb een sorteer functie gemaakt die alle bestanden op extentie en naam sorteert. Alleen nu is het probleem dat het id niet juist is.

Als ik deze met de functie for() uitlees komt het item met "blauwe film.mpg" weer als derde te staan. Zoals ze hier onder afgebeeld staan zijn ze goed maar hoe lees ik ze nu stuk voor stuk uit?

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Array
(
    [0] => Array
        (
            [id] => 113
            [name] => anne.pdf
            [ext] => pdf
            [url] => 113.pdf
            [omschrijving] => 
        )

    [1] => Array
        (
            [id] => 108
            [name] => baukje.pdf
            [ext] => pdf
            [url] => 108.pdf
            [omschrijving] => 
        )

    [3] => Array
        (
            [id] => 111
            [name] => carla.pdf
            [ext] => pdf
            [url] => 111.pdf
            [omschrijving] => 
        )

    [4] => Array
        (
            [id] => 112
            [name] => dropje.pdf
            [ext] => pdf
            [url] => 112.pdf
            [omschrijving] => 
        )

    [2] => Array
        (
            [id] => 151
            [name] => blauwe film.mpg
            [ext] => mpg
            [url] => 151.mpg
            [omschrijving] => 
        )

    [5] => Array
        (
            [id] => 150
            [name] => filmpje.mpg
            [ext] => mpg
            [url] => 150.mpg
            [omschrijving] => This is a movie
        )

    [6] => Array
        (
            [id] => 153
            [name] => korte film met mpeg.mpeg
            [ext] => mpeg
            [url] => 153.mpeg
            [omschrijving] => 
        )

)

[ Voor 7% gewijzigd door Martine op 20-12-2005 00:28 ]


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
Als ik deze met de functie for() uitlees komt het item met "blauwe film.mpg" weer als derde te staan.
Waarschijnlijk omdat je hem aanroept met $array[2]. Heb je al eens naar foreach gekeken?

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
Behalve foreach gebruiken, wat waarschijnlijk sowieso verstandiger is, kun je ook array_values gebruiken om de keys van je buitenste array weer in de goede volgorde te zetten.

Trouwens, zowel for als foreach zijn taalconstructies en geen functies.

[ Voor 17% gewijzigd door Soultaker op 20-12-2005 00:33 ]


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 22:34
Alle wegen leiden naar Rome...
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//met for():
for($subArray=reset($array);$subArray !== FALSE; $subArray = next($array)) {
    print_r($subArray);
}

//met foreach()
forach($array as $subArray) {
    print_r($subArray);
}

//met while
$subArray = reset($array);
print_r($subArray);
while($subArray = next($array)) {
    print_r($subArray);
}

//met do while
$subArray = reset($array);
do {
    print_r($subArray);
} while ($subArray = next($array));

//met while en array_shift();
while ($subArray=array_shift($array)) {
    print_r($subArray);
}

De while, do while en while-shift zijn ongeveer even snel als een foreach op kleine arrays. De for constructie is traag door de evaluatie die elke iteratie moet plaatsvinden. Omwille van leesbaarheid en snelheid lijken me een foreach of while-shift dus het verstandigst.
Op een groot array blijkt echter dat de while-shift() vele malen (500+% boven foreach) sneller is. Als het array maar 1x nodig is zou ik die methode dan ook van harte aan bevelen. Heb je het array vaker nodig dan is een array_values constructie het snelst (5% boven foreach):
PHP:
1
2
3
4
5
6
$sorted = array_values($a);
$count = count($sorted);

for($i=0; $i<$count; $i++) {
    print_r($sorted[$i];
}


Met benchmark cijfers:
Small array ( 5 entries x 5.000 repetitions)
# foreach:      0.295178174973
# array values: 0.344153165817
# for:          0.366385936737
# while:        0.289100170135
# dowhile:      0.282907962799
# shift:        0.299455165863

Big array ( 1000 entries x 1.000 repetitions)
# foreach:       13.4319651127
# array valuesl: 12.7180728912
# for:           17.5526969433
# while:         14.9240808487
# dowhile:       14.6231520176
# shift:         2.33392500877

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
Op een groot array blijkt echter dat de while-shift() vele malen (500+% boven foreach) sneller is.
Ik heb net ook wat benchmarks gedaan, en daar blijkt foreach sneller. Dat zou kunnen liggen aan mijn wat verouderde lokale PHP versie (4.3.3), maar graag zie ik jouw benchmarkscript.
PHP5 ondersteunt trouwens pass-by-reference bij foreach, wat mogelijk voor een snelheidsverbetering zorgt.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:27

.oisyn

Moderator Devschuur®

Demotivational Speaker

is die shift niet veel sneller omdat je de de array leeg maakt na de eerste lus? :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 22:34
GlowMouse schreef op dinsdag 20 december 2005 @ 12:50:
[...]
Ik heb net ook wat benchmarks gedaan, en daar blijkt foreach sneller. Dat zou kunnen liggen aan mijn wat verouderde lokale PHP versie (4.3.3), maar graag zie ik jouw benchmarkscript.
Net zo'n oude PHP hier. Het benchmark script bestaat uit een aantal functies die exact dezelfde output genereren a la:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//foreach
function testForeach($a) {
    $out = array();
    foreach($a as &$v) {
        $out[] = $v;
    }
    return($out);
}

//array shift
function testShift($a) {
    $out=array();
    while ($v=array_shift($a)) {
        $out[] = $v;
    }

    return ($out);
}


Elk van deze functies wordt $iterations maal aangeroepen:
PHP:
1
2
3
4
5
6
$start = getmicrotime();
for ($i=0; $i<$iterations; $i++) {
    testShift($a);
}
$time = getmicrotime() - $start;
echo '<li>shift: ' .$time;

Het complete script kun je hier vinden. Het is niet het netste script (iets met nachtwerk), maar wel gechecked op output. Net ook even getest op m'n linux machiente en daar zijn de resultaten hetzelfde; Bij kleine arrays is foreach licht in het voordeel, bij grote arrays wint de shift dik.
.oisyn schreef op dinsdag 20 december 2005 @ 13:22:
is die shift niet veel sneller omdat je de de array leeg maakt na de eerste lus? :)
Nope, testen heeft in functies plaatsgevonden die met een kopie van het array werken :)

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
In PHP5 is het echt véél trager - ik heb nog twee manieren getest om het te verifiëren.

Mijn testcode:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
$ELEMENTS    = intval($argv[1]);
$EXPERIMENTS = intval($argv[2]);
$METHOD      = intval($argv[3]);

srand(0);

for($experiment = 0; $experiment < $EXPERIMENTS; ++$experiment)
{
        // Create array
        $in = array();
        for($n = 0; $n < $ELEMENTS; ++$n)
                $in[] = rand();
        asort($in);

        switch($METHOD)
        {
        case 1:
                echo "array_values\n";
                $out = array_values($in);
                break;

        case 2:
                echo "array iteration in for loop\n";
                $out = array();
                for($value = reset($in); $value !== FALSE; $value = next($in))
                        $out[] = $value;
                break;

        case 3:
                echo "foreach loop\n";
                $out = array();
                foreach($in as $value)
                        $out[] = $value;
                break;

        case 4:
                echo "array shift in while loop (1)\n";
                $out = array();
                while(count($in) > 0)
                        $out[] = array_shift($in);
                break;

        case 5:
                echo "array shift in while loop (2)\n";
                $out = array();
                while(($value = array_shift($in)) !== NULL)
                        $out[] = $value;
                break;

        default:
                echo "No method selected.\n";
                $out = $in;
        }
}

Ik ga er vanuit dat PHP geen 'slimme' optimalisaties doet en de code dus gewoon uitvoert zoals 'ie er staat.

Voor een array met 10000 elementen en 10 testcases:
MethodeTotale tijdEffectieve tijdRelatieve effectieve tijd
Geen.2.241s0.000s
array_values()2.693s0.452s1.000
array iteratie3.370s1.129s2.498
foreach-lus2.814s0.573s1.268
array shift (1)490.022s487.781s1078.940
array shift (2)490.091s487.850s1079.314

array_values() is dus gewoon het snelste. De foreach-lus daarna (~25% trager) en daarna de array iteratie (~150% trager). De varianten met array_shift zijn duizend keer (!) trager - compleet onbruikbaar dus. Klinkt redelijk conform mijn verwachtingen; waarschijnlijk worden in array_shift intern inderdaad allerlei elementen verschoven.

Ik heb verder niet veel getest met kleine testcases; daar zijn de eerste drie opties ook razendsnel en de laatste twee traag. ;)

[ Voor 35% gewijzigd door Soultaker op 20-12-2005 20:52 . Reden: Beetje husselen met testcases... ]


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22:43
T-MOB schreef op dinsdag 20 december 2005 @ 19:22:
Het complete script kun je hier vinden. Het is niet het netste script (iets met nachtwerk), maar wel gechecked op output. Net ook even getest op m'n linux machiente en daar zijn de resultaten hetzelfde; Bij kleine arrays is foreach licht in het voordeel, bij grote arrays wint de shift dik.
Hehe, er zit een grote bug in je script. ;) De 'big' array initialiseer je als array(rand(0,10000) => 0, rand(0,10000) => 1, rand(0,10000) => 2, .. ,rand(0,10000) => 1000);. Vervolgens doe je while ($v=array_shift($a)), oftewel, je haalt direct de waarde '0' eruit (dat is tenslotte het eerste element) en die evalueert naar 'false'. Die methode doet in dat geval dus helemaal niets.

Verander die regel maar eens in while(($v=array_shift($a)) !== NULL) en test opnieuw. ;)

Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 22:34
Soultaker schreef op dinsdag 20 december 2005 @ 20:47:
Hehe, er zit een grote bug in je script. ;)
Aj, je hebt gelijk. En dan te bedenken dat ik in het kleine array bewust 0 had weggelaten (en dan ook nog het kleine array heb gebruikt om te checken of de output gelijk was). * T-MOB trekt zich terug in zijn hoekje om zich te schamen ;)

Regeren is vooruitschuiven

Pagina: 1