Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006

PHP - Sortera array

Skulle behöva sortera en array, tyvärr inte bara i bokstavsordning.
Ursäkta för det långa inlägget, det mesta är bara luft.

Så här ser arrayn ut för tillfället:

Array ( [0] => Array ( [id] => 1 [name] => Kompetenser [parent] => 0 [depth] => 0 ) [1] => Array ( [id] => 2 [name] => Administration [parent] => 1 [depth] => 1 ) [2] => Array ( [id] => 11 [name] => Organisation [parent] => 2 [depth] => 2 ) [3] => Array ( [id] => 4 [name] => Arbetsledning [parent] => 2 [depth] => 2 ) [4] => Array ( [id] => 17 [name] => Planering [parent] => 2 [depth] => 2 ) [5] => Array ( [id] => 9 [name] => Hantverke [parent] => 1 [depth] => 1 ) [6] => Array ( [id] => 10 [name] => Snickeri [parent] => 9 [depth] => 2 ) [7] => Array ( [id] => 12 [name] => Språk [parent] => 1 [depth] => 1 ) [8] => Array ( [id] => 13 [name] => Tolk [parent] => 12 [depth] => 2 ) [9] => Array ( [id] => 15 [name] => Arabiska [parent] => 13 [depth] => 3 ) [10] => Array ( [id] => 14 [name] => Persiska [parent] => 13 [depth] => 3 ) [11] => Array ( [id] => 16 [name] => Polska [parent] => 13 [depth] => 3 ) [12] => Array ( [id] => 18 [name] => Apotekare [parent] => 1 [depth] => 1 ) [13] => Array ( [id] => 19 [name] => Dotkorand [parent] => 1 [depth] => 1 ) [14] => Array ( [id] => 21 [name] => Atomfysik [parent] => 19 [depth] => 2 ) [15] => Array ( [id] => 20 [name] => Fysik [parent] => 19 [depth] => 2 ) [16] => Array ( [id] => 22 [name] => Ekonom [parent] => 1 [depth] => 1 ) [17] => Array ( [id] => 23 [name] => Industriell ekonomi [parent] => 22 [depth] => 2 ) [18] => Array ( [id] => 24 [name] => Filosofi [parent] => 1 [depth] => 1 ) )

Och så här ska den bli:

Array ( [0] => Array ( [id] => 1 [name] => Kompetenser [parent] => 0 [depth] => 0 ) [1] => Array ( [id] => 2 [name] => Administration [parent] => 1 [depth] => 1 ) [3] => Array ( [id] => 4 [name] => Arbetsledning [parent] => 2 [depth] => 2 ) [2] => Array ( [id] => 11 [name] => Organisation [parent] => 2 [depth] => 2 ) [4] => Array ( [id] => 17 [name] => Planering [parent] => 2 [depth] => 2 ) [12] => Array ( [id] => 18 [name] => Apotekare [parent] => 1 [depth] => 1 ) [13] => Array ( [id] => 19 [name] => Dotkorand [parent] => 1 [depth] => 1 ) [14] => Array ( [id] => 21 [name] => Atomfysik [parent] => 19 [depth] => 2 ) [15] => Array ( [id] => 20 [name] => Fysik [parent] => 19 [depth] => 2 ) [16] => Array ( [id] => 22 [name] => Ekonom [parent] => 1 [depth] => 1 ) [17] => Array ( [id] => 23 [name] => Industriell ekonomi [parent] => 22 [depth] => 2 ) [18] => Array ( [id] => 24 [name] => Filosofi [parent] => 1 [depth] => 1 ) [5] => Array ( [id] => 9 [name] => Hantverke [parent] => 1 [depth] => 1 ) [6] => Array ( [id] => 10 [name] => Snickeri [parent] => 9 [depth] => 2 ) [7] => Array ( [id] => 12 [name] => Språk [parent] => 1 [depth] => 1 ) [8] => Array ( [id] => 13 [name] => Tolk [parent] => 12 [depth] => 2 ) [9] => Array ( [id] => 15 [name] => Arabiska [parent] => 13 [depth] => 3 ) [10] => Array ( [id] => 14 [name] => Persiska [parent] => 13 [depth] => 3 ) [11] => Array ( [id] => 16 [name] => Polska [parent] => 13 [depth] => 3 ) )

Alla med parent 2 ska alltså läggas i bokstavsordning precis efter Administration (id 2) och så vidare.

Så här ser min sorteringsfunktion ut just nu, ful men överskådlig - den sorterar bara efter namn än så länge och anropas via uasort().

function cat_sort($a, $b) { if($a['name'] == $b['name']) { $return = 0; } elseif($a['name'] < $b['name']) { $return = -1; } else { $return = 1; } return $return; }

Någon som har en aning om hur jag kan göra?

Tack på förhand!

Trädvy Permalänk
Medlem
Plats
Göteborg
Registrerad
Maj 2007

Du har väl inte den här datan hårdkodad? Om du lagrar den i en databas, vilket jag skulle gissa på att du gör, borde du sortera datan med SQL. Att skriva en egen algoritm för sorteringen känns bara omständigt, när det är enkelt att göra det med SQL...

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av bjornie
Du har väl inte den här datan hårdkodad? Om du lagrar den i en databas, vilket jag skulle gissa på att du gör, borde du sortera datan med SQL. Att skriva en egen algoritm för sorteringen känns bara omständigt, när det är enkelt att göra det med SQL...

Om du så enkelt kommer på ett sätt att sortera data från nested sets i bokstavsordning i en query så blir du förmodligen hyllad av alla kodare där ute.
Och jag skulle självklart bli väldigt tacksam.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Aug 2002
Citat:

Ursprungligen inskrivet av Ivarska
Om du så enkelt kommer på ett sätt att sortera data från nested sets i bokstavsordning i en query så blir du förmodligen hyllad av alla kodare där ute.
Och jag skulle självklart bli väldigt tacksam.

Om du skriver vilken SQL-sats du använder i dag, så kan vi kanske hjälpa dig.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av DeeXt
Om du skriver vilken SQL-sats du använder i dag, så kan vi kanske hjälpa dig.

Så här ser min funktion ut just nu (queryn ligger där i):

function tree_sorted() { //Get data $query = 'SELECT node.id, node.name, node.parent, (COUNT(parent.name) - 1) AS depth FROM test_competence AS node, test_competence AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.name ORDER BY node.lft'; $result = mysql_query($query) or die(mysql_error()); //Fetch gotten data while($data = mysql_fetch_assoc($result)) { $fetched[] = $data; } //Sort fetched data uasort($fetched, 'cat_sort'); //Return merged data return $fetched; }

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002

Hur lagrar du data i trädet? Lägger du bara till nya noder sist under varje förälder (kronologisk ordning) eller har du en annan sortering? Det lättaste sättet att sortera efter en annan ordning än trädets naturliga (ORDER BY node.lft) är att ha en extra kolumn med den sorteringen som du sedan uppdaterar vid ändringar.

Abstractions all the way down.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av Biberu
Hur lagrar du data i trädet? Lägger du bara till nya noder sist under varje förälder (kronologisk ordning) eller har du en annan sortering? Det lättaste sättet att sortera efter en annan ordning än trädets naturliga (ORDER BY node.lft) är att ha en extra kolumn med den sorteringen som du sedan uppdaterar vid ändringar.

I databasen menar du? Jag slänger bara in det i tabellen med denna funktion:

function add($parent, $node) { global $database; $query = 'LOCK TABLE test_competence WRITE;' . "\n"; $query .= 'SELECT @myLeft := lft, @myRight := rgt - 1 FROM test_competence WHERE id = ' . $parent . ';' . "\n"; $query .= 'SET @angel = IF(@myRight = @myLeft + 2, @myLeft, @myRight);' . "\n"; $query .= 'UPDATE test_competence SET rgt = rgt + 2 WHERE rgt > @angel;' . "\n"; $query .= 'UPDATE test_competence SET lft = lft + 2 WHERE lft > @angel;' . "\n"; $query .= 'INSERT INTO test_competence(name, lft, rgt, parent) VALUES("' . $node . '", @angel + 1, @angel + 2, ' . $parent . ');' . "\n"; $query .= 'UNLOCK TABLES;' . "\n"; return $database->multiple_queries($query); }

Ska köra med stored procedure när jag fått allt att rulla.
Det där med en extra kolumn låter omständigt, men om det fungerar utan att pressa databasen så spelar det ingen roll för mig. Hur blir mina queries då då? Man måste kunna ta bort kompetenser från databasen också (inte fixat den funktionen riktigt än, men det är nästan som på sidan du länkade till förut).

Det var du som drog in mig i det här med nested sets, så nu får du allt hjälpa mig ur det här! Haha. Nä men helt ärligt så håller jag tummarna för att du lyckas hjälpa mig.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006

Kanske går att placera allt rätt i databasen från början (alltså i bokstavsordning)? Vore ändå smidigt om man kunde välja vad man ville sortera på, men det är mest min medarbetare som vill ha den möjligheten.

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002

När du hämtar ut informationen sorterar du efter L för att få den hierarkiska ordningen (O). Om du bara lägger till noder som sista barn per förälder blir datan kronologiskt sorterad. Vill du lagra allt alfabetiskt måste du först ta reda på vilket syskon noden ska ha och sedan placera den med hänsyn till det istället för föräldern.

L R O Node 1 1 12 1 |-- Node 1-1 2 7 2 |-- Node 1-1-1 3 4 3 |-- Node 1-1-2 5 6 4 Node 1-2 8 11 5 |-- Node 1-2-1 9 10 6 L R O Node 1 1 14 1 |-- Node 1-1 2 9 2 |-- Node 1-1-1 3 4 3 <-- Sibling |-- Node 1-1-3 5 6 4 <-- Ny nod |-- Node 1-1-2 7 8 5 <-- L > Sibling.R Uppdateras | R > Sibling.R Uppdateras Node 1-2 10 11 6 |-- Node 1-2-1 12 13 7

För att ha flera sorteringar cachade i samma tabell får du kontrollera vilka ändringar som behöver göras i de kolumnerna (motsv. O) när du uppdaterar den, men om de flesta visningar kommer använda sig av den alfabetiska sorteringen så kanske det räcker att sortera resten per visning eller cacha på annan plats. Om du oftast behöver sortera mindre delar och inte alla kompetenser i listan bör det inte vara något problem med det.

Abstractions all the way down.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006

Osäker om jag förstår tabellen du skrev upp, men själva konceptet tror jag att jag förstår. Men själva queryn har jag ingen aning om hur den ska vara, då måste man väl jämföra med en massa andra nodes?

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002

Yup, du behöver en funktion för att hitta rätt syskon. Den måste hämta ut alla relevanta noder direkt under din nya föräldranod i rätt sortering och sedan stega igenom dom tills den passar in. Sedan får du flytta/skapa noder efter (parent_id,sibling_id,node) istället för bara (parent_id,node).

Abstractions all the way down.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av Biberu
Yup, du behöver en funktion för att hitta rätt syskon. Den måste hämta ut alla relevanta noder direkt under din nya föräldranod i rätt sortering och sedan stega igenom dom tills den passar in. Sedan får du flytta/skapa noder efter (parent_id,sibling_id,node) istället för bara (parent_id,node).

Jobbigt... Och påfrestande för databasen? Finns det inget smidigare sätt att få ordning på kompetenserna?

Trädvy Permalänk
Legendarisk
Hedersmedlem
Plats
::1
Registrerad
Dec 2002

Det är viktigare att optimera läsningar, du kommer inte ändra stukturen så ofta i relation till det. Jag använder en supportklass som sköter allt som påverkar hierarkin, det är en del pyssel att skriva en sådan, men det finns säkert färdiga att ladda ner. Det är säkert lättare att få igång en fungerande lösning där du bara loopar igenom en nivå i taget.

Abstractions all the way down.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Aug 2002

Jag skrev en trädmeny för något år sedan eller så. Den löser visserligen inte problemet med att sortera din array, men den skriver åtminstone ut en trädmeny där barnen är i bokstavsordning. Men förhoppningsvis finns det något att hämta därifrån i alla fall.

http://niklasfors.com/treemenu.php
http://niklasfors.com/treemenu.phps

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av DeeXt
Jag skrev en trädmeny för något år sedan eller så. Den löser visserligen inte problemet med att sortera din array, men den skriver åtminstone ut en trädmeny där barnen är i bokstavsordning. Men förhoppningsvis finns det något att hämta därifrån i alla fall.

http://niklasfors.com/treemenu.php
http://niklasfors.com/treemenu.phps

Ett stort tack till dig!
Med hjälp av din kod lyckades jag få till den kod jag ville ha:

class data_comp { var $fetched_tree = array(); function tree_fetch($parent = 0) { $query = 'SELECT node.id, node.name, node.parent, (COUNT(parent.name) - 1) AS depth FROM test_competence AS node, test_competence AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.name ORDER BY node.name'; $result = mysql_query($query) or die(mysql_error()); $tree = array(); while($data = mysql_fetch_assoc($result)) { $tree[$data['parent']][$data['id']] = array('name' => $data['name'], 'depth' => $data['depth']); } $this->tree_print($tree, $parent); } function tree_print($tree, $parent) { foreach($tree[$parent] as $id => $value) { $this->fetched_tree[] = array('id' => $id, 'name' => $value['name'], 'depth' => $value['depth']); if(isset($tree[$id]) && is_array($tree[$id])) { $this->tree_print($tree, $id); } } } }

Problemet är löst - om någon har åsikter om optimeringar angående detta så är ni välkomna med dem här.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av Biberu
Det är viktigare att optimera läsningar, du kommer inte ändra stukturen så ofta i relation till det. Jag använder en supportklass som sköter allt som påverkar hierarkin, det är en del pyssel att skriva en sådan, men det finns säkert färdiga att ladda ner. Det är säkert lättare att få igång en fungerande lösning där du bara loopar igenom en nivå i taget.

Kanske serialiserar arrayn och sparar i en textfil - är det mer optimalt än ovanstående kod tro?

Trädvy Permalänk
Medlem
Plats
Göteborg
Registrerad
Maj 2007

Ja, det är det - så länge din data inte ändras i databasen. Men då kanske det skulle vara en idé att cacha hela sidan? Jag skrev ihop ett litet exempel för ett tag sedan, här, som är enkelt att implementera.

Trädvy Permalänk
Medlem
Plats
Stockholm
Registrerad
Sep 2006
Citat:

Ursprungligen inskrivet av bjornie
Ja, det är det - så länge din data inte ändras i databasen. Men då kanske det skulle vara en idé att cacha hela sidan? Jag skrev ihop ett litet exempel för ett tag sedan, här, som är enkelt att implementera.

Cachning har jag sysslat med en del förut. Att cacha hela sidor känns bara överflödigt, däremot är det bra att dra ner på antalet anrop mot databasen genom bl a cachning.