Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [Drzewa] Metoda niepowrzucanych węzłów
Asmox
post
Post #1





Grupa: Zarejestrowani
Postów: 359
Pomógł: 12
Dołączył: 16.01.2009

Ostrzeżenie: (0%)
-----


Witajcie
Udało mi się napisać kod, który tworzy wielowymiarową tablicę drzewa. Jest to metoda wymyślona przeze mnie (chyba że ktoś użył jej przede mną, nie wiem po prostu mówię że znikąd nie kopiowałem), więc nie wiem czy ma jakieś szanse w przyszłości. A nóż widelec może by się komuś kiedyś przydało.
Założenia:
1. Baza danych
Baza danych, nie pliki tekstowe, nie XML... bo takie rzeczy się edytuje ręcznie, a w używając bazy można pokombinować z automatyzacją i elementami, które nie wymagają bezpośredniego siedzenia w kodzie
2. Najprostsza tabela drzewa
Potrzebujemy zwykłą tabelę drzewa. Bez żadnych kombinacji, IP, drugiej tabeli powiązań itd... czyli:
Kod
ID | PAR_ID (id rodzica) | LABEL (etykieta elementu, np. nazwa kategorii)

3. Bez rekurencji
W zależności od ustawień serwera PHP, nie można wykonywać funkcji_z_funkcji bez końca. Powodem jest stos przechowujący odwołania do tych wszystkich niedokończonych funkcji, który może się przepełnić. Czasami rekurencja faktycznie się przydaje, ale w moim rozwiązaniu użyłem po prostu while(true)
4. Niepowrzucane węzły
Elementy pośrednie (nie_korzenie i nie_liście, czyli po prostu gałęzie (IMG:style_emoticons/default/biggrin.gif) ) sprawdzają, czy nie są przypadkiem rodzicem dla czegoś w głównej tabeli. Jeżeli są to olewamy, gdyż:
jeśli 1 > 2 > 3
i wrzucimy 2 do 1
to nie będzie jak wrzucić 3 do 2
Podsumowując szukamy i wrzucamy elementy od końca tak, aby zawsze hierarchia była zachowana, czyli
jeśli 3 nie ma dzieci w głównej tabeli to wrzuć 3 do (id_rodzica_trójki), jeżeli nie, to spróbuj z innym elementem
Operujemy tylko na głównej tabeli, jeżeli mamy do przeniesienia rodzica, to razem ze wszystkimi wcześniej utworzonymi zagłębieniami
5. Konwersja na tablicę gotową do wypisania
zgodnie z TYM SPOSOBEM WYPISANIA TABLICY
Czyli że każdy rodzic jest kluczem w tabeli, każdy liść elementem o indeksie typu int
6. Rozbijanie ciągów
Mam świadomość, że coś takiego pewnie znacznie obniża wydajność kodu (w porównaniu z przechowywaniem info jako tablicy), ale to rozwiązanie wiąże się z punktem 5. gdzie wypisanie <li> to zwykłe rozbicie i powkładanie w elementy, a nie szukanie po zagłębionych tablicach


Ok a teraz kod funkcji:
  1. public function naListe() {
  2. $tabelka = SimpleMySQL::getTable('kategorie'); // Pobieramy tabele drzewka
  3. foreach($tabelka as $val) { // Tworzymy tablice jednowymiarowa w stylu: [0] = ID~*~ID_RODZICA~*~ETYKIETA
  4. $arr[] = "{$val[$this->_tableFields['id']]}~*~{$val[$this->_tableFields['parent']]}~*~{$val[$this->_tableFields['label']]}";
  5. }
  6.  
  7. foreach($arr as $val) { // Dokonujemy wstepnej selekcji dzieci koncowych $arr[dane_rodzica] = array([0] = dziecko, [1] = dziecko2 ...)
  8. $parent = false; // Na poczatku zakladamy ze obiekt nie jest rodzicem
  9. $rozbij1 = explode('~*~', $val); // Rozbijanie: 0 = id || 1 = parent || 2 = label
  10. if($rozbij1[1] == null) $parent = true;
  11. foreach($arr as $v) {
  12. $rozbij2 = explode('~*~', $v); // Rozbijanie analogiczne
  13. if($rozbij1[0] == $rozbij2[1]) { // Jesli id I elementu jest jak parent II elementu...
  14. $parent = true; // To I jest rodzicem (bedzie indeksem w tabeli)
  15. break;
  16. }
  17. }
  18. if($parent == true) {
  19. $lista[$val] = array();
  20. }
  21. else {
  22. $lista[] = $val;
  23. }
  24. }
  25.  
  26. // Wrzucamy dzieci koncowe do rodzicow...
  27. $doUsuniecia = array();
  28. foreach($lista as $key => $val) {
  29. if(is_numeric($key) && !is_array($val)) { // Jezeli element ma zwykly indeks i nie jest tablica to na pewno jest to dziecko
  30. $rozbij1 = explode('~*~', $val); // Rozbijanie: 0 = id || 1 = parent || 2 = label
  31. foreach($lista as $k => $v) {
  32. $rozbij2 = explode('~*~', $k); // Rozbijanie analogiczne
  33. if($rozbij1[1] == $rozbij2[0]) { // Jesli indeks rodzica I elementu jest taki sam jak id II elementu to jest on rodzicem
  34. $parent = $k;
  35. break;
  36. }
  37. }
  38. $lista[$parent][] = $val; // Wrzucamy dziecko końcowe (liść) do rodzica pod zwykłym indeksem int
  39. // SORTOWANIE Z TYM MAM PROBLEM
  40. $doUsuniecia[] = $key; // Oznaczamy do wywalenia z głównej tablicy
  41. }
  42. }
  43. // I wywalamy je z glownej tablicy
  44. foreach($doUsuniecia as $val) {
  45. unset($lista[$val]);
  46. }
  47.  
  48.  
  49. // Wstepny podzial zrobiony, zabieramy sie za wrzucanie
  50. while(true) {
  51. $children = array();
  52.  
  53. foreach($lista as $key => $val) {
  54. $parent = false; // Zakładamy na początku, że obiekt nie jest rodzicem, ale to nie istotne bo może się zmienić
  55. $rozbij1 = explode('~*~', $key); // Rozbijanie: 0 = id || 1 = parent || 2 = label (rozbijamy klucz bo to on przechowuje info)
  56. if($rozbij1[1] == null) continue; // Elementy glowne...
  57. foreach($lista as $k => $v) {
  58. $rozbij2 = explode('~*~', $k); // Rozbijanie analogiczne
  59. if($rozbij1[0] == $rozbij2[1]) { // Jesli id I elementu jest jak rodzic II elementu...
  60. $parent = true; // To I jest rodzicem - oznaczamy
  61. break;
  62. }
  63. }
  64. if($parent != true)
  65. $children[] = $key; // Jeżeli nie to wrzucamy do tablicy rodziców, którzy nie mają dzieci w głównej tablicy, czyli są jako dzieci
  66. }
  67.  
  68. if(empty($children)) break; // BARDZO WAŻNA INSTRUKCJA, jeżeli nie ma już dzieci do powrzucania, TO KOŃCZYMY CAŁĄ FUNKCJĘ ROBOTA SKOŃCZONA
  69.  
  70. foreach($children as $val) {
  71. // Znajdujemy pelna nazwe rodzica
  72. $dziecko = explode('~*~', $val);
  73. foreach($lista as $k => $v) {
  74. $mozeRodzic = explode('~*~', $k);
  75. if($dziecko[1] == $mozeRodzic[0]) { // Jesli id_rodzica dziecka jest takie samo jak id dowolnego elementu, to jest on rodzicem
  76. $rodzic = $k; // Pobieramy jego klucz
  77. break;
  78. }
  79. }
  80. // Koniec znajdywania pelnej nazwy rodzica
  81. $lista[$rodzic][$val] = $lista[$val]; // Wrzucamy element ze wszystkimi wewnętrznymi gałęziami do rodzica...
  82. unset($lista[$val]); // ... i usuwamy z głównej tablicy
  83. }
  84.  
  85. } // END WHILE
  86. echo '<hr /><pre>';
  87. print_r($lista);
  88. echo '</pre>';
  89. }

Oto mój wynik:
Kod
Array
(
    [1~*~~*~Komputery] => Array
        (
            [3~*~1~*~Hardware] => Array
                (
                    [0] => 6~*~3~*~P?yty g?ówne
                )

            [2~*~1~*~Software] => Array
                (
                    [0] => 4~*~2~*~Programy
                    [5~*~2~*~Gry] => Array
                        (
                            [0] => 7~*~5~*~RTS
                            [1] => 8~*~5~*~RPG
                        )

                )

        )

    [9~*~~*~Kuchnia] => Array
        (
        )

)

Chętnie poczytam Wasze uwagi i komentarze do tej metody. Jeszcze pracuję nad tym, ale póki co to mam problem z posortowaniem (nawet liści - tam gdzie wstawiłem komentarz o sortowaniu była funkcja sort, która nie działa). Tak więc pomoc mile widziana.
I dzięki że chciało się wam to wszystko czytać (IMG:style_emoticons/default/winksmiley.jpg)

Ten post edytował Asmox 6.08.2010, 07:41:57
Go to the top of the page
+Quote Post

Posty w temacie


Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 24.08.2025 - 13:37