Sjednocování polí

Školení, která pořádám

Pro sjednocování polí se v PHP obvykle používá funkce array_merge. Tato funkce má tu vlastnost, že prvky indexované číslem nepřepisuje, ale naopak je seřadí za sebe. Pokud tuto vlastnost chceme potlačit, musíme se bez této funkce obejít.

<?php
// řekněme, že pole jsou indexovaná číslem výrobku
$pole1 = array(3 => "a", 4 => "b");
$pole2 = array(6 => "c", 7 => "d");

// někdy nepříjemná vlastnost array_merge
$vysledek = array_merge($pole1, $pole2); // array(0 => "a", 1 => "b", 2 => "c", 3 => "d")

// krkolomná náhrada - ještě horší by to bylo, kdyby polí bylo víc
$vysledek = $pole1;
foreach ($pole2 as $key => $val) {
    $vysledek[$key] = $val;
}

// elegantní řešení
$vysledek = $pole1 + $pole2;
?>

O tom, že operátor + se dá použít i u polí a array_merge výborně doplňuje, se moc neví. Stejně tak se ani moc neví, že se pole dají pomocí standardních operátorů také snadno porovnávat.

Jakub Vrána, Výuka, 6.5.2005, diskuse: 15 (nové: 0)

Diskuse

Llaik:

Pokud chcete pracovat s poli, tak rozsahlejsi popis moznosti najdete na prekvapive adrese http://php.net/array :)

Solvina:

Wow! Skvely - sem ani nevedel, ze jde pouzit pole + pole. Diky moc.

ikona spaze:

kdysi ne/davno pred vice nez rokem, za devatero firewally a devatero floody, sem spojoval nejaky pole. Jednalo se tehdy o nejaky hromadny prejmenovani ~8k souboru, byl zapojenej glob(), dotazy do db a buhvico (ale byla to jednorazovka, takze to asi nebylo optimalne napsany ve smyslu algoritmizace), ale to spojovani poli me dostalo

<?php
while ($arr_result = mysql_fetch_assoc($result))
    // i would use array_merge(dbdata, globdata), if it was performing faster :/
    foreach (glob($config['data_dir'].$arr_result['znacka'].'/'.$arr_result['dbpic'].'.{'.implode(',', $config['data_img_ext']).'}', GLOB_BRACE) as $value)
        $arr_dbdata[] = $value;
?>

udelat to pres foreach (a to jeste pred jeho zrychlenim) bylo mnohem, ale opravdu mnohem rychlejsi, s array_merge() to koncilo na standardnim 30sec timeoutu, s foreachem to bylo za chvilku. + jsem tenkrat nejak prehlidl, ci co :)

Jakuje:

Přesně tohle jsem hledal. Na php.net o něčem takovémto není ani zmínka, ale kdesi v houbi jsem tušil že tohle by mělo fungovat. Jen ta lenost zkoušet.
Naštěstí to někdo udělal za mě. Díky.

ikona Jakub Vrána OpenID:

Zmínka na php.net: http://www.php.net/manual/en/language.operators.array.php

petr:

Bohužel sjednocení polí pomocí + fungují jen pro jednorozměrná pole, přesně podle manuálu. Vícerozměrná pole selžou. O tom ale není nikde zmínka. Pak dochází ke ztrátě dat.

petr:

Ještě upřesním:
$a = array("a" => "apple", "b" => "banana");
$b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");

$c = $a + $b; // Union of $a and $b ... platí

$a = array(1=>array("a" => "apple", "b" => "banana"));
$b = array(1=>array("a" => "pear", "b" => "strawberry", "c" => "cherry"));
$c = $a + $b; // Union of $a and $b ... neplatí
chybí pole c=>"cherry"
array_merge provede správně výsledek jako jednorozměrné pole..

ikona v6ak:

Zkus se dívat na vnořené pole jako na "prostě nějakou položku", podobně jako na řetězec. Zkus projet pole pomocí foreach a sjednotit položky, dostaneš jiný výsledek.

petr:

S tím foreach souhlasím, to je metoda, aspoň co znám, že lze provést aktualizaci dat - pole na pole. Trochu to je vyřešeno s tím array_merge, ale zase je tam chyba (možná úmyslná), že když jsou klíče numerické, tak se místo přepisu provede připojení nové položky. V manualu to je uvedeno.

Ale jsem proti matení pojmů. Název pole je obecně vícerozměrné a podmnožinou je pole jednorozměrné. A některé funkce polí php slouží jen pro jednorozměrné pole. Array_merge je ale univerzální i pro vícerozměrné pole, avšak jen pro textové klíče.

Takže mohu sebelíp studovat manuál, ale až teprve příklady uvedou rozdíly v chápání definic. Prostě není pole jako pole. Takže i to + pro sjednocování je jen pro zúženou skupinu případů, nikoliv obecně.

ikona Jakub Vrána OpenID:

Pole je prostě seznam hodnot indexovaných klíčem. Tvůj příklad obsahuje dvě jednoprvkové pole, jejichž sjednocení přesně odpovídá popisu operátoru (existující položky se nepřepisují).

To, že některé funkce pracují rekurzivně, je vlastnost výhradně těchto funkcí. Rekurzivně ale přeci nemusí pracovat všechny funkce (např. readdir).

petr:

Jakube, děkuji za názor, ale nesouhlasím s ním. Hlavně přístupem k definicím. Například v oboru reálných čísel platí, že a + b = c, to znamená, že čísla prostě sečtu a nic více mne nezajímá.
V oboru komplexních čísel se však dopustím hrubé chyby, zanedbáním imaginární části, protože vlastnost komplexních čísel obecně přikazuje pracovat se všemi komponentami čísel.

Podobně je to u polí. Nikde není definice, že "pole" je jen a jen jednorozměrné, čili jak píšeš "seznam hodnot indexovaných klíčem". To je mylné zjednodušení jen pro pochopení principu, podobně jako v oboru reálných čísel.

Podobně mylný názor je "dvě jednoprvkové pole". Ale jaká to jsou jednoprvkové pole"? Opět obdobné zjednodušení. Nemluvím ovšem o vnitřním řešením problému a jeho rozpadu na jednotlivé kroky.

Proto jsem použil větu s matením pojmů. Pole jsou vždy obecně daná množina, jejichž vlastnost se dovím až v její definici, nikoliv že předpokládám nejjednodušší verzi vlastnosti, například reálné číslo, nebo jednorozměrné pole.
Toť z matematické definice pojmu pole.

Právě pak dochází k chybám už v analýze a v názvosloví a nakonec v praxi dochází "nahodile" k chybným výsledkům.

Vím, že zvyk je železná košile, ale mně vždy vtloukali do hlavy "Jaký je definiční obor", abych neudělal chybu. A to je u php popisů nedotažené. Proto mne ten operátor + vedl k této diskusi.

ikona Jakub Vrána OpenID:

Ty máš představu, že PHP pracuje s nějakými vícerozměrnými poli, ve skutečnosti to tak ale není. Pole je v PHP skutečně „seznam hodnot indexovaných klíčem“, hodnotou nicméně může být další pole.

Kvůli tomu vrací <?php count(array(array(1, 2, 3))) ?> jedna a ne tři a konzistentně s tím se chová i operátor plus.

petr:

Dobře Jakube, uvádíš příklad, jak je naprogramováno php. O to mi v podstatě nejde, chápu, že je to vnitřně někdy zjednodušené na to jednorozměrné pole. Ale právě u těch funkcí není jednotnost pro pojem pole.

Count se vykašle na komplexnost pole, ale array_merge je poctivé a projde vše. Tady vidím to matení. Rekurzivnost s tím nemá co do činění.

Zpátky k úvodnímu článku.
Popsána je nejdříve metoda pomocí foreach, která zajistí úplnost i pro vícerozměrné pole, pokud to dobře naprogramuji.
Ale "elegantní řešení" s operátorem + je zavádějící, platí jen pro jednorozměrné pole, čili to není obecná metoda, jako v případě použití foreach. V tom vidím pro čtenáře slepou uličku, kteří mohou "naletět".

ikona Jakub Vrána OpenID:

array_merge() je na tom s poctivostí stejně jako operátor plus. Jediný rozdíl je, že hodnoty s numerickými klíči do výsledného pole zapisuje (plus je přeskakuje). Toto chování je jasně definované a popsané u obou konstrukcí.

array_merge() tedy pole $a a $b „vidí“ stejně jako operátor plus a stejně jako všechny ostatní funkce – jako dvě jednoprvková pole.

ikona David Grudl:

Velmi rychlá implementace pro vícerozměrná pole:

<?php
   
/**
     * Recursively appends elements of remaining keys from the second array to the first.
     * @param  array
     * @param  array
     * @return array
     * @author D.G.
     */
    function arrayAppend($arr1, $arr2)
    {
        $res = $arr1 + $arr2;
        foreach (array_intersect_key($arr1, $arr2) as $k => $v) {
            if (is_array($v) && is_array($arr2[$k])) {
                $res[$k] = arrayAppend($v, $arr2[$k]);
            }
        }
        return $res;
    }
?>

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.