Řazení jednoúrovňového pole rubrika: Programování: PHP

Anonym
položil/-a 1.2.2021
 
upravil/-a 1.2.2021

Zdravím,

snažím se setřídit pole podle dvou klíčů, avšak trochu specifickým způsobem a nedaří se mi to.

Máme pole:

$items = [
  ['id' => 1, 'afterId' => 0],
  ['id' => 2, 'afterId' => 0],
  ['id' => 3, 'afterId' => 0],
  ['id' => 4, 'afterId' => 1],
  ['id' => 5, 'afterId' => 2],
  ['id' => 6, 'afterId' => 3],
];

Potřebuji ho dostat do následujícího stavu:

[
  ['id' => 1, 'afterId' => 0],
  ['id' => 4, 'afterId' => 1],
  ['id' => 2, 'afterId' => 0],
  ['id' => 5, 'afterId' => 2],
  ['id' => 3, 'afterId' => 0],
  ['id' => 6, 'afterId' => 3]
]

Tedy mám specifikované nějaké IDčka a pak afterId, dle kterého by se to mělo řadit pod jednotlivé ID na první úrovni, né stromově pod klíčem toho ID. tzn:

[
  1 => [...],
  2 => [...],
]

je nežádoucí. Nedaří se mi dostat do 100% validního stavu. Pokud například ID = 10 zařadím mezi některé s afterId = 0, tak již algoritmus selže a je to seřazeno nesprávně.

Systém řazení jaký používám:

usort($items, function($a, $b) {
  if ($a->afterId === $b->afterId) {
    return $a->id > $b->id;
  }
 
  return $a->id !== $b->afterId;
});

Nevím jak to udělat lépe.. Asi už mi jen hrabe z toho, jak nad tím furt přemýšlím.. Nevěděl by někdo, jak to udělat na 100%, případně jaký algoritmus by se na tohle řešení dal použít a nasměroval mě? Možná je vůbec ten návrh "afterId" špatný a je třeba nějaká lepší struktura. Moc této problematice nerozumím, tak bych ocenil každou radu. Děkuji.

Komentáře

  • rmaslo : A to afterId může ukazovat na nějaké id, která má zase nenulové afterId ? Tj. mohl by existovat třeba další záznam, který by byl ['id'=>7, 'afterId'=>4] a který by se měl ve výsledku zařadit na třetí pozici? T.j. ten strom má dvě úrovně nebo nekonečnou hloubku? 4.2.2021
odkaz Vyřešeno
3 Fos4
odpověděl/-a 2.2.2021

Chyba je, že callback funkce usort nevrací číslo (-1,0,1 ; menší,rovno,větší) ale boolean.
Takže při "false" to vyhodnotí jako rovno (intval(false) === 0) a neudělá to, co čekáš. Správně by to mělo být takto:

<code>
$items = [
  ['id' => 1, 'afterId' => 0],
  ['id' => 2, 'afterId' => 0],
  ['id' => 3, 'afterId' => 0],
  ['id' => 4, 'afterId' => 1],
  ['id' => 5, 'afterId' => 2],
  ['id' => 6, 'afterId' => 3],
];
 
usort($items, function($a, $b) {
  if ($a['afterId'] === $b['afterId']) {
    return $a['id'] - $b['id'];
  }
 
  return $a['id'] - $b['afterId'];
});
 
 
var_export($items);
// output
array (
  0 =>
    array (
      'id' => 1,
      'afterId' => 0,
    ),
  1 =>
    array (
      'id' => 4,
      'afterId' => 1,
    ),
  2 =>
    array (
      'id' => 2,
      'afterId' => 0,
    ),
  3 =>
    array (
      'id' => 5,
      'afterId' => 2,
    ),
  4 =>
    array (
      'id' => 3,
      'afterId' => 0,
    ),
  5 =>
    array (
      'id' => 6,
      'afterId' => 3,
    ),
)

Komentáře

  • Anonym : Děkuji za odpověď, ale po vyzkoušení to funguje úplně stejně jako to mám já s vracení booleanovské hodnoty. Zakopaný pes bude jinde.. :-/ 2.2.2021
  • Fos4 : Vracení false/true pro callback může občas fungovat (ono se to sejde že to dobře dopadne), ale je to chybně. Co jsem si vzal data tak mně to normálně projde a výstup se shoduje se zadáním (celý kód jsem kvůli formátování přidal do mé původní odpovědi). Zkus jestli nemáš chybu někde jinde, tady to sedí - možná jiná data (v případě že $a['id'] se rovná $b['afterId'] může být problém a chtělo vrátit -1/+1) 2.2.2021
  • Anonym : No to funguje, pokud jsou takto seřazeny.. Pokud v budoucnu některý odmažu a přidám další a začne se to míchat, tak to již nefunguje.. Tohle ukázkové pole se tahá z databáze.. Udělat sem to tedy tak, že se pozice zapisuje do databáze už při vytváření a přepočítávají se pozice i ostatních prvků.. Asi mě to mělo napadnout i dříve.. Je to určitě i rychlejší.. Jelikož dané řazení se mělo provádět ještě v rekurzivní funkci .. Je to takový komplikovanější případ :-D .. Ale děkuji za pomoc :-) 4.2.2021

Pro plný přístup na Devel.cz se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.