Arrays begrijpen: foreach met if
Ik ben al wel een tijd bezig met php, maar heb nooit echt iets met arrays gedaan. Nu wil ik wat wiskunde gaan doen met php en een soort van voorwaardes in een foreach-lus zetten die vergelijkbaar zijn met een WHERE-clausule uit een sql-query.
Ik zet om te beginnen een veld uit een databank in een array. Vervolgens draai ik daar een foreach-lus overheen waarin ik alle data die niet aan mijn voorwaardes voldoen weer uit het array haal. Uiteindelijk print ik het array weer om te kijken wat er in zit.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
$SlotArray = array();
$sql = "SELECT * FROM aex";
$exec_sql = mysql_query($sql) or die("Foutmelding: ".mysql_error());
while($z = mysql_fetch_assoc($exec_sql)){
$SlotArray[$z['Datum']] = $z['Slot'];
}
reset($SlotArray);
foreach($SlotArray as $waarde){
if($waarde > 300 OR $waarde < 290){
unset($SlotArray[key($SlotArray)]);
}
}
print_r($SlotArray);
$sql = "SELECT * FROM aex";
$exec_sql = mysql_query($sql) or die("Foutmelding: ".mysql_error());
while($z = mysql_fetch_assoc($exec_sql)){
$SlotArray[$z['Datum']] = $z['Slot'];
}
reset($SlotArray);
foreach($SlotArray as $waarde){
if($waarde > 300 OR $waarde < 290){
unset($SlotArray[key($SlotArray)]);
}
}
print_r($SlotArray);
De eerste 10 resultaten van het array zijn echter de volgende:
[1983-01-03] => 45.38
[2011-02-25] => 366.77
[2011-02-28] => 369.13
[2011-03-01] => 367.95
[2011-03-02] => 364.86
[2011-03-03] => 367.60
[2011-03-04] => 367.95
[2011-03-07] => 366.08
[2011-03-08] => 366.38
[2011-03-09] => 366.25
Er zit dus ergens iets fout. Ik begrijp echter absoluut niet waar. Heeft iemand enig idee waar ik fout zit?
ps. Ik weet dat ik natuurlijk ook gewoon een "WHERE Slot < 300 AND Slot > 290" kan toevoegen aan de sql-query. In een later stadium in mijn berekeningen zal dat echter niet meer kunnen. Bovendien wil ik ook gewoon wat meer leren werken met arrays.. :-)
Gewijzigd op 23/11/2011 10:12:46 door Peter Sanders
Code (php)
1
2
3
4
5
2
3
4
5
foreach($SlotArray as $k => $waarde){
if($waarde > 300 OR $waarde < 290){
unset($SlotArray[$k]);
}
}
if($waarde > 300 OR $waarde < 290){
unset($SlotArray[$k]);
}
}
Op deze manier haal je dus zowel de waarde van het huidige element op (in $waarde), als de key (in $k).
Dank! Die oplossing werkt inderdaad perfect. Handig inderdaad dat ik dan gewoon de key en de waarde allebei apart in een variabele heb. Zoals gezegd, ik begin net met arrays, dus ik moet het concept nog een beetje in mijn hoofd krijgen.
Nog een ander vraagje. Als ik nou een twee-dimensionaal array heb. Hoe zet ik dan de beide keys in een variabele?
Dus ik heb bijvoorbeeld een twee-dimensionaal array waarin één key de datum is, en één key de tijd. De inhoud van het array is de koers van de aex op dat tijdstip.
Dus zoiets:
$koers['1996-12-27']['16:35:00'] = 290.01;
$koers['1996-12-27']['16:36:00'] = 290.46;
Hoe zou ik dit nu inladen in een foreach-lus? Ik heb het volgende geprobeerd.
Code (php)
1
2
3
2
3
foreach($koers as $datum => $tijd => $waarde){
echo "op ".$datum." om ".$tijd." was de koers van de aex ".$waarde;
}
echo "op ".$datum." om ".$tijd." was de koers van de aex ".$waarde;
}
Daarop krijg ik echter een foutmelding: Parse error: syntax error, unexpected T_DOUBLE_ARROW, expecting ')'
Enige tip hoe ik dit zou kunnen doen?
Gewijzigd op 23/11/2011 10:54:02 door Peter Sanders
Als ik zo vrij mag zijn om nog twee vraagjes te stellen (ik leer zo lekker veel hier);
1) Is het mogelijk om met dat laatste array alle resultaten uit 2004 te verwijderen? Dus iets in de trant van
Code (php)
Ik begrijp alleen niet hoe je een wildcard doet zoals je dat in sql met een % doet. Jullie (of iemand anders) toevallig enig idee?
Vraag 2) Is het mogelijk om met de resultaten uit een array, de resultaten uit een ander array op te halen? Zo heb ik twee arrays:
$aex['1996-12-27']['16:35:00'] = 290.01;
$aex['1996-12-27']['16:36:00'] = 290.46;
etc.
en
$dowjones['1996-12-27']['16:35:00'] = 2090;
$dowjones['1996-12-27']['16:36:00'] = 2095;
Nu wil ik alle elementen uit het $aex-array unsetten waarvan de datum niet voorkomt in het $dowjones-array. Dus zoiets als dit:
Code (php)
Maar dat zou volgens mij alles unsetten, aangezien er voor elke $aex_datum wel een $dowjones_datum is die er niet aan gelijk is. Enig idee hoe ik dit zou kunnen oplossen?
Gewijzigd op 23/11/2011 11:35:57 door Peter Sanders
if( substr($datum,0,4) == '2004' )
Moet je wel alle elementen door. Ik ken geen simpelere oplossing (wat niet betekent dat die er niet is natuurlijk).
Vraag 2: Op die manier is het heel er langzaam ( O(n*m) )en waarschijnlijk wordt alles eruitgehaald.
Wat je wel kan, met de functie isset() is checken of key "aex_datum" bestaat in de array $dowjones. Dus:
Code (php)
Nu weet ik alleen niet hoe isset precies door de array heen gaat en daarmee of het enigszins snel is. Misschien is het van dezelfde orde.
Als je de arrays gesorteerd op datum krijgt (bijvoorbeeld uit de database) dan kan het wel veel sneller ( O(n+m) ). Begin bij beide arrays bij het eerste element, check of ze gelijk zijn. Zo nee, controleer of de aex_datum lager is dan de dowjones datum. Zo ja, verwijder de aex waarde en ga in die array naar de volgende. Zo nee, ga in de dowjones array naar de volgende. Zijn beide data wel gelijk, ga in beide arrays naar de volgende waarde. Ga zo door tot je het laatste element heb gehad in de aex array
In pseudo code:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
reset(aex_array);
reset(dowjones_array);
while ( !end of array(aex_array) ){
if (aex_array[datum] == dowjones[datum]){
go to next element in aex_array, go to next element in dowjones_array
} else {
if (aex_array[datum] < dowjones[datum]){
unset(aex_array[datum])
go to next element in aex_array
} else {
go to next element in dowjones_array
}
}
}
reset(dowjones_array);
while ( !end of array(aex_array) ){
if (aex_array[datum] == dowjones[datum]){
go to next element in aex_array, go to next element in dowjones_array
} else {
if (aex_array[datum] < dowjones[datum]){
unset(aex_array[datum])
go to next element in aex_array
} else {
go to next element in dowjones_array
}
}
}
Peter Sanders op 23/11/2011 11:34:26:
Om alles uit 2004 te verwijderen heb je maar één loop nodig omdat je dan de datum al weet.
Code (php)
Peter Sanders op 23/11/2011 11:34:26:
Vraag 2) Is het mogelijk om met de resultaten uit een array, de resultaten uit een ander array op te halen? Zo heb ik twee arrays:
$aex['1996-12-27']['16:35:00'] = 290.01;
$aex['1996-12-27']['16:36:00'] = 290.46;
etc.
en
$dowjones['1996-12-27']['16:35:00'] = 2090;
$dowjones['1996-12-27']['16:36:00'] = 2095;
Nu wil ik alle elementen uit het $aex-array unsetten waarvan de datum niet voorkomt in het $dowjones-array. Dus zoiets als dit:
Maar dat zou volgens mij alles unsetten, aangezien er voor elke $aex_datum wel een $dowjones_datum is die er niet aan gelijk is. Enig idee hoe ik dit zou kunnen oplossen?
$aex['1996-12-27']['16:35:00'] = 290.01;
$aex['1996-12-27']['16:36:00'] = 290.46;
etc.
en
$dowjones['1996-12-27']['16:35:00'] = 2090;
$dowjones['1996-12-27']['16:36:00'] = 2095;
Nu wil ik alle elementen uit het $aex-array unsetten waarvan de datum niet voorkomt in het $dowjones-array. Dus zoiets als dit:
Code (php)
Maar dat zou volgens mij alles unsetten, aangezien er voor elke $aex_datum wel een $dowjones_datum is die er niet aan gelijk is. Enig idee hoe ik dit zou kunnen oplossen?
Je kijkt of de datum een key is in het andere array().
Zo niet, verwijderen.
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
foreach($aex as $aex_datum => $value)
{
if(!in_array($aex_datum, array_keys($dowjones)))
{
unset($aex[$aex_datum]);
}
}
?>
foreach($aex as $aex_datum => $value)
{
if(!in_array($aex_datum, array_keys($dowjones)))
{
unset($aex[$aex_datum]);
}
}
?>
Over vraag 2) Het zal uiteindelijk om een redelijk groot array gaan (ongeveer 3,6 miljoen waardes) waar ik mbv een paar (zelfgeschreven) functies een aantal berekeningen op wil doen. Die berekeningen zal ik ook nog ongeveer een miljoen keer moeten loopen, dus het zal al met al wel eventjes duren. Ik denk echter dat dit sneller zal gaan dan met sql aangezien ik zo alles in het geheugen heb staan en het daardoor naar mijn weten vooral zwaar is voor de CPU.
Ik zal in ieder geval eens wat tests doen met isset, en kijken hoe lang dat duurt. Als dat erg lang is dan zal ik het proberen met het tweede idee wat je voorsteld (wat ik overigens redelijk briljant vind).
Heel erg bedankt in ieder geval!
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
$keys = array_keys($dowjones);
foreach($aex as $aex_datum => $value)
{
if(!in_array($aex_datum, $keys))
{
unset($aex[$aex_datum]);
}
}
?>
$keys = array_keys($dowjones);
foreach($aex as $aex_datum => $value)
{
if(!in_array($aex_datum, $keys))
{
unset($aex[$aex_datum]);
}
}
?>
Code (php)
Gewijzigd op 23/11/2011 12:52:34 door Ger van Steenderen
Ik had jouw reacties nog niet gezien toen ik mijn bovenstaande reactie schreef. Jouw oplossing ziet er inderdaad wel erg goed uit. Ik ga er eens wat tests mee doen.
Ik kwam ook nog de functie array_key_exists tegen. Misschien dat ik het daarmee ook wel zou kunnen doen?
Ik heb in ieder geval weer genoeg lees- en probeerwerk.. :-)
Dank aan jullie beiden!
Ziet er goed uit, alleen blijft het wel de vraag hoe snel het is. Weet jij hoe in_array (net als isset) werkt?
Mijn ervaring met een aantal voorgebouwde php array functies is is dat ze geweldig werken op kleine arrays, maar zodra het een beetje groot wordt (in de orde van 100.000 elementen) dan wordt het erg langzaam.