[PHP] unset in een array (maar dan anders) *

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Ik heb een n-dimensionale array, en daar wil ik een waarde uit unsetten, bijv.

PHP:
1
unset($myArray[0][3][6][2]);


simpel, een kind kan de was doen.

Nu heb ik echter de waarden 0, 3, 6, 2 in een array staan.

PHP:
1
$keyArray=array(0, 3, 6, 2);


en ben ik op zoek naar een functie $keyArray als argument heeft, maar uiteindelijk

PHP:
1
unset($myArray[0][3][6][2]);


doet. Ik zou echter niet weten hoe dit aan te pakken. Heb al zitten kijken naar array_diff, maar daarbij moeten de waarden van beide arrays overeenkomen.
PHP-manual
Twee elementen worden gezien als hetzelfde als en alleen als (string) $elem1 === (string) $elem2. In woorden: wanneer de representatie als string hetzelfde is.
Dit is bij mij niet het geval: ik wil gewoon dat hij de array met de bewuste key-combinatie verwijdert, ongeacht wat voor waarde het is.

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Een dergelijke functie is wel te maken, maar weet je zeker dat je zo goed bezig bent? Weet je zeker dat je dit soort trucs over een jaar nog begrijpt? Kan deze datastructuur niet beter?

{signature}


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Voutloos schreef op maandag 18 februari 2008 @ 13:59:
Een dergelijke functie is wel te maken
Nee hoor. Probeer maar 'ns :) .edit: Hmm, het is een array, dan werkt unset zowaar nog als verwacht in een functie

[ Voor 20% gewijzigd door .oisyn op 18-02-2008 14:09 ]

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!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Het kan wel en is niet eens zoveel werk, maar je zal wel gauw naar wazige constructies als $$ moeten grijpen. En op het moment dat je dat ziet aankomen, mag je jezelf dus meteen de vragen stellen uit m'n eerste post. En als het om $$ en eval() constructies gaat, kan je die vragen altijd wel snel beantwoorden. :)

{signature}


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$keyArray = array(0, 2, 3, 5);

$array[0][2][3][5] = 'bla';
$array[0][2][3][6] = 'foo';

$element = & $array;

foreach($keyArray as $index) {
  if(is_array($element[$index]))
    $element  = & $element[$index];
  else
    unset($element[$index]);
}

print_r($array); // Laat enkel element foo zien

:Y)

Verdraaid lastig algoritme om te maken nog, vooral omdat unset($element) de reference unset, niet de array key die'ie referenced, en je dus $element[$index] moet gebruiken. Nochtans sluit ik me wel bij Voutloos aan, waarom denk je ooit zoiets nodig te hebben? :?

[ Voor 36% gewijzigd door FragFrog op 18-02-2008 14:18 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • steffex
  • Registratie: Augustus 2003
  • Laatst online: 12-08 00:24
als je array met de cijfers altijd even groot is, dan zou je dit kunnen doen:

PHP:
1
2
3
4
<?php
$keyArray=array(0, 3, 6, 2);
unset($myArray[$keyArray[0]][$keyArray[1]][$keyArray[2]][$keyArray[3]]);
?>


dit is niet de meest ideale optie, maar genoeg als de situatie ongewijzigd blijft.

[ Voor 2% gewijzigd door steffex op 18-02-2008 14:18 . Reden: typo ]


Acties:
  • 0 Henk 'm!

  • remmelt
  • Registratie: Januari 2001
  • Laatst online: 09-04 12:25
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function unsetFromArray($keys, $deleteFromMe) {
    $key = array_shift($keys);

   if(isset($deleteFromMe[$key])){
    if(is_array($keys) && count($keys)){
        $deleteFromMe[$key] = unsetFromArray($keys, $deleteFromMe[$key]);
    } else {
        unset($deleteFromMe[$key]);
    }
   }

   return $deleteFromMe;
}

$keys = array(0, 3, 6, 2);
$myArray = array(array(7,8,9,array(13,14,15,16,17,18,array(19,20,"iwanttobedeleted",21,22,23,24)),10,11,12), 1,2,3,4,5,6);

echo "<pre>";
var_dump($myArray);
$myArray = unsetFromArray($keys, $myArray);
var_dump($myArray);


Bedenk wel dat je recursiviteit moet begrijpen om recursiviteit te begrijpen.

[ Voor 0% gewijzigd door een moderator op 18-02-2008 14:30 . Reden: Code tags aangepast, syntax highlighting FTW \0/ ]


Acties:
  • 0 Henk 'm!

  • remmelt
  • Registratie: Januari 2001
  • Laatst online: 09-04 12:25
Nette cleane oplossing, FragFrog! Die ampersand vergeet ik veel te vaak te gebruiken.

edit:
FragFrog schreef op maandag 18 februari 2008 @ 14:17:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$keyArray = array(0, 2, 3, 5);

$array[0][2][3][5] = 'bla';
$array[0][2][3][6] = 'foo';

$element = & $array;

foreach($keyArray as $index) {
  if(is_array($element[$index]))
    $element  = & $element[$index];
  else
    unset($element[$index]);
}

print_r($array); // Laat enkel element foo zien
Werkt toch niet helemaal, je is_array() zorgt ervoor dat je geen arrays kan wissen uit het multidimensionale origineel. Je kan in dit geval dus niet een heel menupad wissen uit de boom, enkel de bladeren.

[ Voor 94% gewijzigd door remmelt op 18-02-2008 15:02 ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
@fragFog: bedankt! _/-\o_

Inmiddels ben ik ook gaan twijfelen of ik iets niet heel anders aan moet pakken. Ik cache een erg grote array in de database, omdat het verkrijgen van die array erg veel SQL-queries kost. Het namelijk een soort menustructuur met soms wel 5 lagen, die 'plat' in de db is opgeslagen.

Ik wil echter niet bij iedere aanpassing in de menustructuur de hele array opnieuw opbouwen vanuit de db (kost erg veel resources). Dus leek het me een goed plan om (naast de db) de gecachte array direct te updaten. Weliswaar doe je het updaten dan dubbelop, maar het is wel een stuk sneller. Jouw functie gebruik ik dan als er een menu-item wordt verwijderd.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

FragFrog schreef op maandag 18 februari 2008 @ 14:17:
Verdraaid lastig algoritme om te maken nog, vooral omdat unset($element) de reference unset, niet de array key die'ie referenced
Dat was idd de issue waar ik aan dacht. Maar met de array als reference aan de functie, en de laatste index ook echt als subscript te gebruiken ipv daar ook nog eens een reference van te maken kan het precies :)

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!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
remmelt schreef op maandag 18 februari 2008 @ 14:30:
Werkt toch niet helemaal, je is_array() zorgt ervoor dat je geen arrays kan wissen uit het multidimensionale origineel. Je kan in dit geval dus niet een heel menupad wissen uit de boom, enkel de bladeren.
Triviale aanpassing. In de loop enkel de reference aanpassen als is_array en anders de loop beeindigen, en enkel als álle iteraties gedaan zijn een unset doen.
:Y)
Dat laatste fixt ook een serieuze bug. Als je $wa[1][2][3] wil unsetten, en $wa[1] is geen array, wordt nu unset($wa[1]) gedaan. ;)
Rekcor schreef op maandag 18 februari 2008 @ 14:37:
Ik wil echter niet bij iedere aanpassing in de menustructuur de hele array opnieuw opbouwen vanuit de db (kost erg veel resources). Dus leek het me een goed plan om (naast de db) de gecachte array direct te updaten. Weliswaar doe je het updaten dan dubbelop, maar het is wel een stuk sneller.
Als het menu aangepast wordt in een admin actie en dit zelden gebeurd: gewoon lekker opnieuw cache vullen, scheelt je een hoop kansen op bugs ivm dubbele logica. :)
Indien het vaker gebeurd: menu efficienter opbouwen. :+

{signature}


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
remmelt schreef op maandag 18 februari 2008 @ 14:30:
Werkt toch niet helemaal, je is_array() zorgt ervoor dat je geen arrays kan wissen uit het multidimensionale origineel. Je kan in dit geval dus niet een heel menupad wissen uit de boom, enkel de bladeren.
Had ik ook aan zitten denken, maar is op zich vrij simpel op te lossen. Gebruik in plaats van foreach en is_array een while en array_pop om te bepalen of je bij het laatste element zit wat je wilt unsetten :)
Voutloos schreef op maandag 18 februari 2008 @ 15:19:
Dat laatste fixt ook een serieuze bug. Als je $wa[1][2][3] wil unsetten, en $wa[1] is geen array, wordt nu unset($wa[1]) gedaan. ;)
Als $wa[1] geen array is bestaat $wa[1][2][3] ook niet dus maakt dat ook niet uit ;)

Tenminste, ik ga er vanuit dat als je een object array_access laat implementeren het nog steeds true oplevert in is_array.. Interessante vraag, nauwelijks relevant :+

[ Voor 32% gewijzigd door FragFrog op 18-02-2008 15:24 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Eh... just for the record, wat is nu de uiteindelijke goede functie?

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Rekcor schreef op maandag 18 februari 2008 @ 15:23:
Eh... just for the record, wat is nu de uiteindelijke goede functie?
Zowel ik als FragFrog beschrijven een fix. Doe eens een zelf een eerste poging?
FragFrog schreef op maandag 18 februari 2008 @ 15:22:
Als $wa[1] geen array is bestaat $wa[1][2][3] ook niet dus maakt dat ook niet uit ;)
De unset is niet nodig, want $wa[1][2][3] bestaat inderdaad niet. Echter is unset($wa[1]) wel duidelijk ongewenst gedrag.

edit:
@hieronder: Zoals gezegd beschrijven we allebei een fix. ;)

[ Voor 56% gewijzigd door Voutloos op 18-02-2008 15:30 ]

{signature}


Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Voutloos schreef op maandag 18 februari 2008 @ 15:24:
De unset is niet nodig, want $wa[1][2][3] bestaat inderdaad niet. Echter is unset($wa[1]) wel duidelijk ongewenst gedrag.
Goed punt, maargoed, ook dat fix je door over de keys array te loopen om te bepalen of je al bij het laatste element bent :)

offtopic:
@ hierboven: die beide essentieel hetzelfde zijn ;) Alleen kan die met mij met minder code gedaan worden >:)

[ Voor 15% gewijzigd door FragFrog op 18-02-2008 15:33 ]

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • Rekcor
  • Registratie: Februari 2005
  • Laatst online: 05-09 21:08
Ik zei 'for the record', omdat ik e.e.a. zelf nu heel anders gaan doen (zoals ik ook eerder aangaf) ;)

Ik hou alleen niet zo van onaffe topics

8)7 <-- Rekcor

Acties:
  • 0 Henk 'm!

  • FragFrog
  • Registratie: September 2001
  • Laatst online: 09:34
Mijn "uiteindelijke" oplossing:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 *  Remove (unset) an array element whose path is given by keyArray
 *  
 *  @param  & $array     A reference to the array to search trough.
 *  @param  $pathArray   The path to the element to remove.
 *  @return Mixed        Either void on succes or false on faillure.      
**/
function removeKey (array & $array, array $pathArray) { 
  while(sizeOf($pathArray) > 1) {
    $index = array_shift($pathArray);
    if(!isset($array[$index]))
      return false;
    $array  =& $array[$index];
  }  
  unset($array[current($pathArray)]);
}


En alternatief met de fix die Voutloos omschrijft:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
function removeKey (array & $array, array $pathArray) { 
  foreach($pathArray as $key => $index) {
    if(is_array($array[$index]))
      $array  =& $array[$index];
    else
      continue;
  }
  end($pathArray);
  if(key($pathArray) == $key && isset($array[$index]))
    unset($array[$index]);
  else
    return false;
}


Zoals al eerder opgemerkt zorgt is_array voor een fundamentele fout, namelijk dat hij enkel breakt als de waarde geen array is - waarmee je dus niet een hele tak tegelijk uit je array kan halen. Dit zou je nog kunnen fixen met een prev($pathArray) maar je kan evengoed mijn oplossing gebruiken die het sowieso wel doet :+

Het lastige is dat je, omdat je geen unset(& $reference) kan doen, niet het laatste element moet hebben maar het een-na-laatste. Tenzij iemand hier iets op weet is controleren of er meer dan 1 element nog in je path zit de simpelste fix volgens mij :)

[ Site ] [ twitch ] [ jijbuis ]


Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Een alternatief voor de hele methode (ik zou 4-d arrays eigenlijk alleen gebruiken bij vierdimensionele data zoals een 3-d landschap door de tijd oid) is het gebruik van boomstructuren in objecten, waardoor je een object X hebt met daarin een aantal gelijksoortige objecten te hangen. Maar da's persoonlijk - met wat gesleutel en afhankelijk van de toepassing kan een 4-d array een stuk sneller zijn.
Pagina: 1