Permalänk
Medlem

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!

Permalänk
Medlem

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...

Permalänk
Medlem
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.

Permalänk
Medlem
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.

Permalänk
Medlem
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; }

Permalänk
Legendarisk

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.

Visa signatur

Abstractions all the way down.

Permalänk
Medlem
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.

Permalänk
Medlem

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.

Permalänk
Legendarisk

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.

Visa signatur

Abstractions all the way down.

Permalänk
Medlem

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?

Permalänk
Legendarisk

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).

Visa signatur

Abstractions all the way down.

Permalänk
Medlem
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?

Permalänk
Legendarisk

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.

Visa signatur

Abstractions all the way down.

Permalänk
Medlem

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

Permalänk
Medlem
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.

Permalänk
Medlem
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?

Permalänk
Medlem

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.

Permalänk
Medlem
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.