• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

[PHP] in_array noch mals per array filtern

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
Hey Ihrs,

ich hab eigentlich ein ziemlich simples Problem. Lese eine große Datenbank aus. Nun prüfe ich per

[src=php]
if in_array("X", $zeile) {
[mach wat]
}
[/src]

ob in den Spalten der Wert X vorhanden ist. Nun will ich das aber nicht am Stück ausgeben sondern gepaart zu passenden Gruppen. Klar, ich könnte nun auch pro Gruppe eine eigene Abfrage machen und diese dann so ausgeben, wenn ich die Abfrage aber schon habe, dachte ich, ich könnte die dann ja weiter verwenden.

Mir kam auch die Idee das ich in in_array() ja nach einem neuen array filtern könnte. Quasi so:

[src=php]
$filter = array('a', 'b', 'c', 'd');
if in_array($filter, $zeile) {
[mach wat]
}
[/src]
Das scheint aber nicht zu klappen. Laut php.net -> Beispiel #3 in_array() mit Array als Suchwert sollte das doch aber klappen!?

Jemand dazu ne Idee?

Gruß godlike
 

evillive

EXIL

Registriert
24 Juli 2013
Beiträge
930
handelt es sich bei $zeile um ein Array?

mysql_fetch_object z.B. gibt dir ein Objekt zurück
wenn du mysqli_result::fetch_assoc verwendest dann wird ein Array zurück geben...

kA was du verwendest aber vllt ist es das Problem
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #3
Ja, $zeile ist ein Array. Kommt von meiner Haupt-DB abfrage. In_array() funktioniert ja auch wunderbar wenn ich z.B. nach "X" suche. Ich will aber mehrere Suchbegriffe dafür hernehmen. Laut php.net sollte also ein zweiter array() funktionieren. Oder hab ich da irgendwo einen Denkfehler?

kann leider gerade nicht weiter schauen da ich im Zug sitze ;)

Gruß godlike
 

epiphora

aus Plastik
Veteran

Registriert
14 Juli 2013
Beiträge
3.894
Ort
DE-CIX
Laut php.net -> Beispiel #3 in_array() mit Array als Suchwert sollte das doch aber klappen!?

Das klappt dort, weil der zweite Parameter ein zweidimensionales Array ist. Es wird also überprüft, ob das Array aus dem ersten Parameter vollständig im zweiten Array enthalten ist. Insofern ist das wieder das gleiche wie das erste Beispiel mit dem String, nur mit Arrays als innerer Datentyp.

Ich verstehe Dein Vorhaben allerdings ohnehin nicht so ganz. Willst Du abfragen, ob die Werte in der Zeile vorhanden sind, unabhängig davon, aus welcher Spalte sie stammen? Wäre es nicht besser, die Spalten direkt abzufragen?

Wie auch immer, für Dein Vorhaben fallen mir zwei Möglichkeiten ein.

1. Du könntest Dir einfach eine Hilfsfunktion schreiben:

PHP:
function all_in_array($needle, $haystack){
	foreach($needle as $value){
		if(!in_array($value, $haystack)){
			return false;
		}
	}
	return true;
}

2. Du prüfst, ob die Schnittmenge der beiden Arrays die gleiche Anzahl an Elementen hat wie Dein Filter:

PHP:
if(count(array_intersect($zeile, $filter)) == count($filter)) {
    // mach wat
 

evillive

EXIL

Registriert
24 Juli 2013
Beiträge
930
zu dem Beispiel von der php Seite

dort wird das array $a angelegt, das dann so aussieht:

[src=php]array(3) {
[0]=>
array(2) {
[0]=>
string(1) "p"
[1]=>
string(1) "h"
}
[1]=>
array(2) {
[0]=>
string(1) "p"
[1]=>
string(1) "r"
}
[2]=>
string(1) "o"
}
[/src]

das Array besteht aus 3 Elementen, wobei zwei davon wieder array's sind. Die Funktion macht nicht das was du willst ;)

[src=php]if (in_array(array('p', 'h'), $a)) {
echo "'ph' gefunden\n";
[/src]

Er schaut also in dem Array nach ob es dort ein Array gibt, das genau so ist wie das gesuchte. Also im Prinzip

[src=php]if ( in_array(array('p', 'h') == $a[0]) [/src]
 

keksautomat

Neu angemeldet

Registriert
15 Juli 2013
Beiträge
471
Schneller geht es hier mit:

[src=php]public static function recursiveArraySearch($needle, $haystack)
{
foreach ($haystack as $key => $value) {
$current_key = $key;
if ($needle === $value OR (is_array($value) && self::recursiveArraySearch($needle, $value) !== false)) {
return $current_key;
}
}
return false;
}
//$this->recursiveArraySearch("X", $array);
//key index oder false[/src]


Bzw wenn du nur nach einem Key suchst, und deren Werte haben willst:

[src=php]public static function array_value_recursive($key, array $arr){
$val = array();
array_walk_recursive($arr, function($v, $k) use($key, &$val){
if($k == $key) array_push($val, $v);
});
return count($val) > 1 ? $val : array_pop($val);
}

//$this->array_value_recursive("label", $array);
//return 1d array mit Wert(en)[/src]
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #7
Ich verstehe Dein Vorhaben allerdings ohnehin nicht so ganz. Willst Du abfragen, ob die Werte in der Zeile vorhanden sind, unabhängig davon, aus welcher Spalte sie stammen? Wäre es nicht besser, die Spalten direkt abzufragen?
Mein Vorhaben ist in groben Zügen folgendes:

Habe eine Webseite mit DB, aus den Daten die ich einerseits für die Webseite nutze möchte ich im Backend per HTML5 eBooks erstellen (Parameter an der URL bestimmen hier welche Infos in die eBooks kommen). Die Datenbank besteht aus ~ 260 Spalten. Ein paar enthalten Texte, die meisten fungieren aber nur als Ja/Nein-Felder (X = Ja, kein X = Nein) und zahlen (Fließkomma) sowie Ja/Nein + Fließkommazahlen. Nun sind die Ausgaben welche ich für meine HTML5-Ausgabe benötige in verschiedene Blocks unterteilt. Das $zeile[] Array enthält einfach die komplette Abfrage der DB. Bei der Ausgabe werden jetzt also auch Spalten ausgegeben die bei Zeile XY keinen Wert (X oder Zahl) enthalten. Sprich die leer sind.

Diese Spalten wollte ich im Vorfeld mit dem $filter herausfiltern ($name ist der Spaltenname meiner DB). So das ich pro Block einfach angeben kann wlche Spalten er in meiner folgenden foreach-Schleife ausgeben kann. Das ganze werde ich jetzt wohl aber einfach mit mehreren SQL-Abfragen lösen. $sql_block1, $sql_block2, $sql_block3 usw. So habe ich dann einfach schon anfangs die korrekten Spalten die ich dann nach in_array("X", $sql_block1) usw. durchsuchen kann. Habe mir nur gewundert wieso das mit dem zweiten Array nicht geklappt hat.

also quasi

[src=php]$sql = "SELECT tabelle1.*, tabelle2.* FROM tabelle1 LEFT JOIN tabelle2 ON tabelle1.url = tabelle2.url WHERE tabelle1.aktiv='1' AND tabelle1.url='".mysql_real_escape_string($url)."'";[/src]

weiter unten will ich dann nicht alle spalten ausgeben sondern halt nur z.b. spalte 4 bis 9, 12, 25 bis 40. Darum wollte ich die Spaltennamen per Array/Filter in die in_array() Funktion quetschen.

So brauche ich ja für jeden Block eine eigene Abfrage wo ich die Spalten auch explizit in der SQL-Abfrage angebe...

Hoffe das war einigermaßen verständlich ;)
 
Zuletzt bearbeitet:

werner

Suchtspielmacher (ehm.)

Registriert
20 Juli 2014
Beiträge
743
Ort
Mannheim
godlike, funktioniert es jetzt?
In deinem Beispiel oben hast du

[src=php]if(in_array($filter, $zeile))[/src]
aber laut php muss der Filter der zweite Parameter sein

[src=php]if(in_array($zeile, $filter))[/src]

Oder bin ich damit auf einem komplett anderem Boot? :D (Gibt es die Redewendung überhaupt?)
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #9
Die reihenfolge war ja schon richtig da

[src=php]bool in_array(mixed $needle, array $haystack [, bool $strict = FALSE])[/src]

$needle ist das wonach du im $haystack suchst.
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #10
Ok, hab es gelöst. Eigentlich simpel, keine Ahnung wieso ich da nicht gleich drauf gekommen bin :coffee:

Ich habe ja 2 Arrays. Ein Array wird aus dem Query erzeugt -> $zeile, einen zweiten definiere ich ja selbst -> $filter

Da ich ja die Schnittmenge für meine Block-Ausgabe benötige vergleiche ich die zwei Arrays einfach mit array_intersect_assoc($filter1, $zeile). Da ich davor per in_array("X", $zeile) nur Inhalte mit "X" zulasse, reicht es meinem $filter-Array jeweils noch "X" zuzuweisen:

[src=php]
$sql = "SELECT * FROM db WHERE ...";
$erg = mysql_query($sql);
if (!$erg) {
die('Ungültige Abfrage: ' . mysql_error());
}
$zeile = mysql_fetch_array($erg, MYSQL_ASSOC);

[...]

$filter = array('a' => 'X', 'b' => 'X', 'c' => 'X', 'd' => 'X', 'e' => 'X', 'f' => 'X');

if (in_array("X", $zeile)) {
$fields = array_intersect_assoc($filter, $zeile);
foreach (($fields ) as $name => $value) {
echo $name;
}
}
[/src]
So werden also nur die Spalten ausgegeben welche 1. einen Wert X besitzen und zweitens ebenso in meinem Filter-Array vorkommen. Sprich ich kann mit verschiedenen Filter-Arrays gewünschte Spalten/Blöcke ausgeben. Genau das was ich wollte :T
 

keksautomat

Neu angemeldet

Registriert
15 Juli 2013
Beiträge
471
Warum nutzt du die Funktionen nicht, die ich oben gepostet habe? Da hinter hättest du dann auch nochmal filtern können, wie du willst..?
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #12
Hey keksautomat,

hatte mit deiner Funktion gestern schon getestet, aber nie eine Ausgabe bekommen. Wie muss $needle denn aufgebaut sein?

Die Funktion läuft im Übrigen in einer while-Schleife ab da ich pro Datensatz aus der DB andere Spalten filtern muss (Datensatz 1 hat z.B: bei a, c, f, und p ein X. Datensatz b bei a, b, f usw. Habe folgendes getestet:

[src=php]
while ($zeile = mysql_fetch_array($erg, MYSQL_ASSOC)) {
recursiveArraySearch($filter, $zeile);
}
[/src]

Wobei sich die Funktion sowie $filter ja außerhalb der Schleife befinden.

edit: müsste sich die Funktion dann nicht innerhalb der Schleife befinden? Muss ich die dann per $i -> $i++ hoch zählen?

Habe heute morgen dann spontan die Idee mit array_intersect_assoc() gehabt und hab das Andere dabei total vergessen :coffee:
 
Zuletzt bearbeitet:

keksautomat

Neu angemeldet

Registriert
15 Juli 2013
Beiträge
471
Nehmen wir folgendes Array

[src=php]array (size=757)
0 =>
array (size=2)
0 =>
array (size=2)
'id' => int 385350
'value' => string '10152' (length=5)
'name' => string 'component_id' (length=12)
1 =>
array (size=2)
0 =>
array (size=2)
'id' => int 384959
'value' => string '10182' (length=5)
'name' => string 'component_id' (length=12)
2 =>
array (size=2)
0 =>
array (size=2)
'id' => int 460522
'value' => string '10231' (length=5)
'name' => string 'component_id' (length=12)
3 => ...[/src]

Und du willst da nur den "value" Part raushaben. Alles andere ist dir egal, dann nutzt du

[src=php]array_value_recursive("value", $array);[/src]

Output:

[src=php] 0 => string '10152' (length=5)
1 => string '10182' (length=5)
2 => string '10231' (length=5)
3 => string '10248' (length=5)
...[/src]

Da durch kannst du dann ja filtern. (mit in_array zum Beispiel)
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #14
Irgendwie steige ich da nicht ganz durch :confused:

Ich habe also zwei Arrays. $needle und $heystack. Ich benötige einerseits alle Schlüssel + alle Werte. Ich bekomme aus der DB ja eine Ausgabe die ~ so ausieht:

[src=php]$array1 = array('a' => 'X', 'b' => 'X', 'c' => '0,5', 'd' => '', 'e' => 'X', 'f' => '', 'g' => 'X', 'h' => '20');
$array2 = array('a' => 'X', 'b' => '0', 'c' => '0', 'd' => '', 'e' => 'X', 'f' => '', 'g' => '', 'h' => '');[/src]

also

[src=php]Array ( [a] => X => X [c] => 0,5 [d] => [e] => X [f] => [g] => X [h] => 20 )[/src]

und $filter, der ja folgendes ausgibt:

[src=php]Array ( [a] => X => 0 [c] => 0 [d] => [e] => X [f] => [g] => [h] => )[/src]

Ich brauche dann quasi alle Schlüssel bei denen die Werte bei beiden Arrays sich überschneiden. (Die Schlüssel sind außerdem die Spaltenbezeichnungen der DB und gleichzeitig die Namen der Grafiken die ich am Ende ausgebe.

Bei meinem Beispiel wäre das folgendes:

[src=php]Array ( [a] => X [d] => [e] => X [f] => )[/src]

wenn ich nach "X" filtere bleibt noch a und e über. Einzig wegen den Werten größer 0 muss ich mal noch schauen. Das klappt in meiner Version auch noch nicht.

Mit deinem Beispiel komme ich hier irgendwie nicht weiter. Ich will ja kein bestimmtes "value" filtern sondern einen ganzen Array.

Oder ich steh bei deiner Funktion auch einfach auf dem Schlauch :confused:
 
Zuletzt bearbeitet:

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Irgendwie steige ich da nicht ganz durch :confused:
Da bist du nicht der einzige, ich finde diesen Thread äußerst verwirrend.

Also erstmal: Wenn du einen Filter hast, in dem die Spalten eingetragen sind, die du nutzen willst, iterier doch über den Filter:

PHP:
$zeile = array('a' => 'X', 'b' => '', 'c' = '3.14', 'd' => 'foo');
$filter = array('a', 'd');

function array_filter_keys($filter, $zeile) {
    $result = array();
    foreach($filter as $key) {
        if (isset($array[$key]) {
            $result[$key] = $zeile[$key];
        }
    }
    return $result;
}


Prinzipiell kannst du so auch einen komplexen Filter bauen, der auch die Werte in Betracht zieht.
Zum Beispiel mit Closures... und statischen Factories die häufig genutzte Closures produzieren.

PHP:
class Filter {
    public static function eq($y) {
        return function($x) use ($y) {
            return $x === $y;
        };
    };

    public static function in($y) {
        return function($x) use ($y) {
            return in_array($x, $y);
        };
    };

    public static function num() {
        return function($x) {
            return is_numeric($x);
        };
    };

    private $config = [];

    public function configure($config) {
        $this->_config = $config;
    }

    public function __invoke($array) {
        $result = [];
        foreach($this->config as $key => $validator) {
            if (isset($array[$key]) && $validator($array[$key])) {
                $result[$key] = $array[$key];
            }  
        }
        return $result;
    }
}
PHP:
$filter = new CallbackFilter();
$filter->configure([
    'a' = $filter::eq('X'),
    'b' = $filter::in(['foo', 'bar']),
    'c' = $filter::num(),
]);

while ($zeile = mysql_fetch_array($erg, MYSQL_ASSOC)) {
    $filtered = $filter($zeile);
    ...
}

und bei vielen Spalten hilft einem dann array_fill_keys:
PHP:
$filter = new CallbackFilter();
$filter->configure([
    array_fill_keys(['a', 'u', 'x'], $filter::num()) +
    array_fill_keys(['c', 'd', 'y'], $filter::eq('X')) +
    array_fill_keys(['n', 'g', 'b'], function($x) { return $x instanceof NGB; })
);


PS: Ich habe gerade keinen Interpreter zur Hand, der Code is "as is" und könnte explodieren. :D
 
Zuletzt bearbeitet:

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #16
Wenn du einen Filter hast, in dem die Spalten eingetragen sind, die du nutzen willst, iterier doch über den Filter:
Die Werte sind dabei für mich zwingend notwendig. Die Spalte ist später für die angezeigte Grafik zuständig, der Wert bestimmt ob und wieso diese angezeigt wird.

Prinzipiell kannst du so auch einen komplexen Filter bauen, der auch die Werte in Betracht zieht.
Zum Beispiel mit Closures... und statischen Factories die häufig genutzte Closures produzieren.
Und würdest du das meinem bisherigen Code vorziehen?`Bei dir ist das ja schon erheblich mehr an Code. Also jetzt wegen der Performance und so.

Wie gesagt, es funktioniert momentan mit folgendem Konstrukt:


[src=php]$array1 = array('a' => 'X', 'b' => 'X', 'c' => '0,5', 'd' => '', 'e' => 'X', 'f' => '', 'g' => 'X', 'h' => '20');
$erg_array1 = mysql_query($array1);
if (!$erg_array1) {
die('Ungültige Abfrage: ' . mysql_error());
}
$zeile = mysql_fetch_array($erg_array1, MYSQL_ASSOC);

while ($zeile = mysql_fetch_array($erg, MYSQL_ASSOC)) {
$array2 = array('a' => 'X', 'b' => $zeile['b'], 'c' => $zeile['c'], 'd' => '', 'e' => 'X', 'f' => '', 'g' => '', 'h' => $zeile['h']);
// die $zeile['xyz'] sind in der while schleife da sich diese felder bei jedem Datensatz ändern

$fields = array_intersect_assoc($array2, $zeile);
foreach (($fields ) as $name => $value) {
if (($value > 0) or ($value == 'X')) {
echo '<img src="http://www.domain.de/gfx/symbole/'.$name.'.png"> ';
}
}
}[/src]

Bitte nicht hauen :cool:

Funktioniert aber perfekt!
 

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Und würdest du das meinem bisherigen Code vorziehen?`Bei dir ist das ja schon erheblich mehr an Code. Also jetzt wegen der Performance und so.
Performance ist nicht unbedingt eine Frage der Codezeilen ;-) Aber: solang das array nicht tausende von Einträgen hat ist das eh eine Microoptimierung, sofern nicht unsinnige innere Schleifen drin sind. In deinem Fall ist es performancetechnisch sicher wichtiger die Zahl der DB Abfragen gering zu halten.

Und würdest du das meinem bisherigen Code vorziehen?
Klar, ist schließlich mein Code :D Spaß bei Seite: mein Ziel war es eine leicht anpassbare und möglichst generische Filterlogik zu haben. Ihre Stärke zeigt sie dann, wenn sie häufig wieder verwendet wird -> Filter configurieren und ausführen. fertig.

PHP:
$filter = new CallbackFilter();
$filter->configure(
    array_fill_keys(['a', 'e'],  $filter::eq('X')) +
    array_fill_keys(['b', 'c', 'h'], function($x) { return ($x > 0) || ($x === 'X'); })
);

while ($zeile = mysql_fetch_array($erg, MYSQL_ASSOC)) {
    foreach ($filter($zeile) as $name => $value) {
        echo '<img src="http://www.domain.de/gfx/symbole/'.$name.'.png"> ';
    }
}

Wäre die Anwendung auf deinen Fall. Die Spalten d,f und g können im übrigen nie die Bedingung leer und (größer 0 oder gleich 'X') erfüllen.

Wenn du für a und e auch Zahlen zulässt und den closure als xnum aufnimmst, sieht es so aus:

PHP:
$filter->configure(array_fill_keys(['a', 'e', 'b', 'c', 'h'], $filter::xnum()));
Wobei, wenn das der einzige Closure bleibt, natürlich die Sinnfrage berechtigt ist ;-)

Btw in Zeile 8 wird $zeile aus 6 überschrieben. Wozu ist Zeile 6 gedacht?
 

godlike

Warp drölf
Veteran

Registriert
13 Juli 2013
Beiträge
14.327
Ort
Topkekistan
  • Thread Starter Thread Starter
  • #18
In deinem Fall ist es performancetechnisch sicher wichtiger die Zahl der DB Abfragen gering zu halten.
Genau deswegen wollte ich ja auch mehrere DB-Abfragen verzichten und das ganze so lösen wie ich eben jetzt mache.



PS: Habe zwischenzeitlich noch mit einer Bekannten geredet. Die hat mir folgendes noch vorgeschlagen:

[src=php]$newArray = array();
foreach ($array1 as $name => $value) {
if(isset($array2[$name])) {
if($array1[$name] == $array2[$name]){
$newArray[$name] = $value;
if (($value > 0) or ($value == 'X')) {
echo '<img src="http://www.domain.de/symbole/'.$name.'.png"> ';
}
}
}
}
[/src]
Klappt auch. Allerdings muss ich hier den Array wieder umdrehen da er quasi rückwärts raus kommt.

Danke schon mal für dein Beispiel, bedenke das ich hier nur mit kleinen Beispielen zeigen wollte was ich vor habe. Mein echter $array1 ist ~ 260 Einträge lang. Da kommt in unregelmäßigen abfolgen alles kreuz und quer vor. Fließkommazahlen, X, (X + Fließkommazahl, X ohne Fließkommazahl, X und Leer [da muss ich jeweils zwei Spalten abfragen die nur in Kombination ein TRUE oder FALS ergeben dürfen) usw. Meinst du es macht Sinn das alles in deinen CallbackFilter() zu packen? Im Endeffekt bleibt die (weitgehend echt beschissene) Datenbankstruktur die Selbe, daran kann ich leider auch nichts ändern. Ich muss daraus einfach ~4-5 Blöcke an Daten ziehen die ich eben verschieden Filtern wollte.

Bei den zwei Beispiel-Mini-Arrays sieht dein Beispiel in der Tat bequem aus, ich befürchte aber das ich mir mit meinem Quell-Array ein wahres Monstrum zusammen bauen werde :D

Btw in Zeile 8 wird $zeile aus 6 überschrieben. Wozu ist Zeile 6 gedacht?
Da hast du Recht, habe ich ganz übersehen :m Hab es mal entfernt :T

PS: Hab die Laufzeiten meiner zwei bisherigen Versionen mal getestet.

Version aus #16: 0.00596690177917 Sekunden
Version aus #18: 0.00865793228149 Sekunden

Schwankt alles im allen aber gewaltig. Denke das ist echt total Wurst...
 
Zuletzt bearbeitet:

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Ich muss daraus einfach ~4-5 Blöcke an Daten ziehen die ich eben verschieden Filtern wollte.
Das ist der Punkt, für den ich dieses Design gut fand: Man deklariert die Spalte und Logik wie die Werte darin zu behandeln ist und bekommt den Teil zurück, für den die Logik true ausgibt.

Natürlich kann es andere Dinge nicht: Paarweise Validierung: Spalte a nur ausgeben, wenn a und b beide X sind.
Oder Blockauswahl: Wenn Spalte a gleich X dann selektiere a,b,c und d.

Also zurück ans Reißbrett. Neuer Ansatz ist kein Filter, sondern ein Selector.

[src=php]class CallbackSelector {
private $config = [];

public function configure($config) {
$this->_config = $config;
}

public function __invoke($array) {
$result = [];
foreach($this->config as list($selector, $selection)) {
$result += $selector($array, array_flip($selection)));
}
return array_intersect_key($array, $result);
}
}[/src]! foreach(... as list(...)) ist PHP 5.5 !

So nun zu den Closures. Deren Interface hat sich geändert: sie bekommen jetzt das Array und ihre Selection (Performance siehe: copy on write) und müssen ein array ausgeben. Der $key der Config spielt keine Rolle mehr stattdessen muss ein Selector und seine Selection angegeben werden.

Blöcke anhand einer X Spalte auswählen:
[src=php]/**
* @return Closure a selector that fires if the key column is 'X'
*/
public static function ifX($key) {
return function ($array, $selection) use ($key) {
return (isset($array[$key]) && $array[$key] === 'X') ? $selection : [];
}
}[/src]

Blöcke anhand einer X Spalte und einer Float Spalte auswählen
[src=php]/**
* @return Closure a selector that fires if the $x column is 'X' and the $f column is > 0;
*/
public static function ifXF($x, $f) {
return function ($array, $selection) use ($x, $f) {
return (isset($array[$x]) && $array[$x] === 'X' && isset($array[$f]) && $array[$f] > 0) ? $selection : [] ;
}
}[/src]

Bisher haben die Selectoren die Selection nur durchgeschleust.
[src=php]/**
* @return Closure a selector that returns all columns in selection that are 'X'
*/
public static function anyX() {
return function ($array, $selection) {
return array_filter(array_intersect_key($array, $selection), function($value) { return $value === 'X'; });
}
}[/src]
[src=php]/**
* @return Closure a selector that returns pairwise true columns in selection
* Requires an array of pairs as selection - ! unusual interface !
*/
public static function pairs() {
return function ($array, $selection) {
$result = [];
foreach($selection as list($x,$f)) {
if (isset(array($x)) && array($x) && isset(array($x)) && array($x)) {
$result += [$x => array($x), $y => array($y)];
}
return $result;
}
}[/src]


Nur so nebenbei: X oder Float oder XFloat aber nicht '', false und 0 lässt sich mit einem cast auf bool sehr einfach prüfen ;-)
Aber weil die Idee ja nicht ist, dass der Code lesbar ist, frisst dieser Einzeiler String und String[].
[src=php]public static function ifTrue($columns){return function ($array, $selection) use ($columns) {return (is_string($columns) && isset($array[$columns]) && $array[$columns]) || (is_array($columns) && array_reduce($columns,function($carry, $c) { return $carry &= (isset($array[$c]) && $array[$c]);},true)) ? $selection : [];}}[/src]


j.k.[src=php]/**
* @param string[] $columns or one column name as a string.
* @return Closure a selector that fires if all $columns cast to true
*/
public static function ifTrue($columns) {
return function ($array, $selection) use ($columns) {
return (
is_string($columns) && isset($array[$columns]) && $array[$columns]
) || (
is_array($columns) && array_reduce($columns,
function($carry, $c) { return $carry &= (isset($array[$c]) && $array[$c]);},
true
)
) ? $selection : [];
}
}[/src]


Configuration: Die Grundidee ist, dass die Config 'lesbar' ist. pairs() gefällt mir nicht, sagt nichts darüber aus, wie die Paare geprüft werden. Aber bothIfBothTrue() mag ich auch net.
[src=php]$select = new CallbackSelector();
$select->configure([
[$select::ifX('a'), ['b','c','d']],
[$select::ifXF('e','f'), ['e']],
[$select::anyX(), ['g','h']],
[$select::pairs(), [['ö','ä'],['q','p']],
[$select::ifTrue('fun'), ['n','g','b']]
]);[/src]

Die Selektionen können sich überschneiden: auch wenn g nicht X ist, wenn $array['fun'] == true, dann n, g, b!


Ich hoffe ich konnte dich mit etwas late night coding inspirieren. Sinn macht das ganze configure Zeug natürlich nur, wenn du mehrere Configurationen hast. Und auch die Closures werden erst dann richtig nützlich, wenn man für eine spezielle Config neue Logik injizieren will, ohne den CallbackSelector selbst zu verändern.

Rant:
Man das ganze auch ausbreiten:
[$select::ifX('a'),...] wird zu $this->ifX('a', ['b','c','d']) und statt Closure Factories gibt es private Methoden. Dann werfen wir das configure weg und die __invoke wird zur statischen filter($array) die zeilenweise die in Funktionsaufrufe umgewandelte config enthält.

Es ist immernoch lesbar und für Dinge wie Wiederverwertbarkeit und Erweiterbarkeit gibt es ja Vererbung (statt dem vereinfachtem Strategy-Pattern). Außerdem ist das eh voll überbewertet: Warum Code schreiben, der wiederverwertbar ist, wenn man nicht plant ihn in noch mal woanders zu verwenden? Ist genau wie Generalisieren: Viel zu aufwendig um ein konkretes Problem zu lösen.

Schließlich kann man noch die Funktionsaufrufe durch deren Body ersetzen und die nutzlose Klasse ist weg, es bleibt filter($array). Und jenen Body dann einfach zwischen die Datenbanklogik in die while schieben. Bei 260 Spalten können wir neben Wiederverwertbarkeit und Erweiterbarkeit dann auch mal über Wartbarkeit reden ;-)

Ich weiß nicht, ob dir all das etwas nützt, aber ich hatte auf jeden Fall meinen Spaß daran :D Gute Nacht.

- Shodan
 
Zuletzt bearbeitet:

Shodan

runs on biochips

Registriert
14 Juli 2013
Beiträge
661
Ort
Citadel Station
Weil ihr euch alle bedankt habt, will ich den im Rant beschriebenen Weg mal gehen

[src=php]function filter($array) {
$select = [];

if ($array['someColumnNameGoesHere'] === 'X') $select += [
'Configs sind was schönes'
'denn wenn einen der Code beim Lesen des Codes stört',
'dann schreibt man ihn halt woanders hin'
];

$select += array_filter([
'Ihr habt euch nicht ernsthaft,'
'mit Wiederverwendbarkeit Ködern lassen, oder?'
'Wo ist denn bitte die Wiederverwendbarkeit'
'der Closure Factory ifX?',
], function($key) use (&$array) {
return ($array[$key] === 'X' || $array[$key] > 0);
});

if ($array['thisOneIsX'] === 'X' || $array['andThisOneIsFloat'] > 0) $select += [
'und was die Generalisierung angeht:'
'wohin soll ich ifX denn generalisieren',
'wenn nicht zu if($condition)',
];

if ($array['fun']) $select += ['n','g','b'];

return array_intersect($array,array_flip($select));
}[/src]

Mit ausnahme von X ist alles was rot ist ein Spaltenname. -> voll unübersichtlich :D
Und dann erst diese kopfgesteuerten if Blöcke in einer Funktion mit ner fußgesteuerten array_filter.. geht gar nicht, das versteht doch keiner mehr.

Generalisieren ist toll, aber wenn den Lösungansatz soweit generalisiert hat, dass man das ursprüngliche Problem jetzt in einer anderen, weniger mächtigen Sprache lösen muss.. macht man was falsch ;-)

In dem Sinne habe ich mir für euch noch was schönes ausgedacht:
[src=php]class ArrayDB {
protected $from;
protected $selection;
protected $result;
public function __construct($from){
$this->from = $from;
$this->selection = [];
$this->result = [];
}
public function select($select) {
$this->selection = array_flip($select);
return $this;
}
public function when($condition) {
if($condition) $this->result += array_intersect_key($this->from, $this->selection);
}
public function filter($closure) {
$this->result += array_filter(array_intersect_key($this->from, $this->selection), $closure);
}
public function getResult() {
return $this->result;
}
}[/src]
Dank dieser tollen Klasse kann man das Problem jetzt mit einer SQL ähnlichen Syntax lösen :D
[src=php]$filter = function($zeile) {
$adb = new ArrayDB($zeile);
$adb->select(['a', 'b'])
->when($zeile['a'] == 'X');
$adb->select(['c', 'd'])
->when($zeile['e'] == 'X' && $zeile['f'] > 0);
$adb->select(['g', 'h', 'j'])
->filter(function($v) {return $v === 'X' || $v > 0;});
$adb->select(['n', 'g', 'b'])
->when(true);
return $adb->getResult();
}[/src]
 
Zuletzt bearbeitet:
Oben