डेटाबेस परिणाम से बहुआयामी सरणी उत्पन्न करने के लिए पुनरावर्ती कार्य


82

मैं एक फ़ंक्शन लिखने के लिए देख रहा हूं, जो पृष्ठों / श्रेणियों (एक फ्लैट डेटाबेस परिणाम से) की एक सरणी लेता है और मूल आईडी पर आधारित नेस्टेड पृष्ठ / श्रेणी आइटम की एक सरणी उत्पन्न करता है। मैं इसे पुनरावर्ती रूप से करना चाहूंगा, ताकि किसी भी स्तर के घोंसले का शिकार हो सके।

उदाहरण के लिए: मैं सभी पृष्ठों को एक क्वेरी में ला रहा हूं, और यह वह है जो डेटाबेस तालिका जैसा दिखता है

+-------+---------------+---------------------------+
|   id  |   parent_id   |           title           |
+-------+---------------+---------------------------+
|   1   |       0       |   Parent Page             |
|   2   |       1       |   Sub Page                |
|   3   |       2       |   Sub Sub Page            |
|   4   |       0       |   Another Parent Page     |
+-------+---------------+---------------------------+

और यह वह सरणी है जिसे मैं अपने दृश्य फ़ाइलों में संसाधित करना चाहता हूं:

Array
(
    [0] => Array
        (
            [id] => 1
            [parent_id] => 0
            [title] => Parent Page
            [children] => Array
                        (
                            [0] => Array
                                (
                                    [id] => 2
                                    [parent_id] => 1
                                    [title] => Sub Page
                                    [children] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [id] => 3
                                                            [parent_id] => 1
                                                            [title] => Sub Sub Page
                                                        )
                                                )
                                )
                        )
        )
    [1] => Array
        (
            [id] => 4
            [parent_id] => 0
            [title] => Another Parent Page
        )
)

मैंने देखा है और लगभग हर समाधान की कोशिश की है जो मुझे आया है (स्टैक ओवरफ्लो पर यहां उनमें से बहुत कुछ है, लेकिन कोई सौभाग्य नहीं मिला है जो कुछ सामान्य रूप से पर्याप्त हो जो दोनों पृष्ठों और श्रेणियों के लिए काम करेगा।

यहाँ मैं निकटतम हो गया हूँ, लेकिन यह काम नहीं करता क्योंकि मैं बच्चों को पहले स्तर के माता-पिता को सौंप रहा हूँ।

function page_walk($array, $parent_id = FALSE)
{   
    $organized_pages = array();

    $children = array();

    foreach($array as $index => $page)
    {
        if ( $page['parent_id'] == 0) // No, just spit it out and you're done
        {
            $organized_pages[$index] = $page;
        }
        else // If it does, 
        {       
            $organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id);
        }
    }

    return $organized_pages;
}

function page_list($array)
{       
    $fakepages = array();
    $fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page');
    $fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page');
    $fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page');
    $fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page');

    $pages = $this->page_walk($fakepages, 0);

    print_r($pages);
}

1
क्या आप अपने पृष्ठों के लिए केवल सभी parent_ids की एक सरणी और किसी अन्य सरणी के साथ काम नहीं कर सकते?
djot

जवाबों:


235

कुछ बहुत ही सरल, सामान्य वृक्ष निर्माण:

function buildTree(array $elements, $parentId = 0) {
    $branch = array();

    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($elements, $element['id']);
            if ($children) {
                $element['children'] = $children;
            }
            $branch[] = $element;
        }
    }

    return $branch;
}

$tree = buildTree($rows);

एल्गोरिथ्म बहुत आसान है:

  1. सभी तत्वों की सरणी और वर्तमान माता-पिता की आईडी लें (शुरू में 0/ कुछ भी नहीं / null/ जो भी हो)।
  2. सभी तत्वों के माध्यम से लूप।
  3. यदि parent_idकिसी तत्व का तत्व आपको वर्तमान माता-पिता की आईडी से मिला है, तो वह तत्व माता-पिता की संतान है। इसे अपने वर्तमान बच्चों की सूची में रखें (यहाँ $branch:)।
  4. उस फ़ंक्शन की आईडी के साथ पुनरावर्ती रूप से कॉल करें जिसे आपने अभी पहचाना है 3., अर्थात उस तत्व के सभी बच्चों को ढूंढें, और उन्हें childrenतत्व के रूप में जोड़ें ।
  5. अपने पाए गए बच्चों की सूची वापस करें।

दूसरे शब्दों में, इस फ़ंक्शन का एक निष्पादन उन तत्वों की एक सूची देता है जो दिए गए मूल आईडी के बच्चे हैं। इसके साथ कॉल करें buildTree($myArray, 1), यह उन तत्वों की सूची लौटाएगा जिनके पास पैरेंट आईडी है। प्रारंभ में इस फ़ंक्शन को पैरेंट आईडी 0 कहा जाता है, इसलिए बिना पैरेंट आईडी वाले तत्व वापस कर दिए जाते हैं, जो रूट नोड हैं। फ़ंक्शन बच्चों के बच्चों को खोजने के लिए खुद को पुनरावर्ती कहता है।


1
खुशी है कि यह मदद करता है। ध्यान दें: यह कुछ हद तक अक्षम है क्योंकि यह हमेशा पूरे $elementsसरणी को नीचे से गुजरता है । छोटे सरणियों के लिए जो शायद ही मायने रखती है, लेकिन बड़े डेटा सेटों के लिए आप इसे नीचे से गुजरने से पहले ही पहले से मिलान किए गए तत्व को निकालना चाहते हैं। हालांकि यह कुछ गड़बड़ हो जाता है, इसलिए मैंने आपकी आसान समझ के लिए इसे सरल छोड़ दिया। :)
छल

6
@ डिसेज़ मैं गन्दा संस्करण भी देखना चाहूंगा। अग्रिम में धन्यवाद!
जेन्स टॉर्नेल

कृपया किसी को समझा सकते हैं कि buildTree () में पहला तर्क 'सरणी' क्या है? क्या यह वह संस्करण होना चाहिए जो आप पृष्ठों के प्रारंभिक सरणी आदि को देते हैं, जैसे '$ पेड़ = सरणी'? क्या कोई अंतिम पंक्ति '$ ट्री = बिल्डट्री ($ पंक्तियों)' को भी समझा सकता है, क्योंकि '$ पंक्तियों' को कहीं भी परिभाषित नहीं किया गया है? अंत में मैं नेस्टेड सूची बनाने के लिए HTML मार्कअप के साथ संघर्ष कर रहा हूं।
22

1
@user arrayएक प्रकार का संकेत है $elements, पहला तर्क बस है array $elements$rowsसवाल से डेटाबेस परिणामों की एक सरणी है।
deceze

1
@user मैंने एक स्पष्टीकरण जोड़ा है। $children = buildTree(...)भाग को अनदेखा करें और फ़ंक्शन बहुत स्पष्ट और सरल होना चाहिए।
deceze

13

मुझे पता है कि यह प्रश्न पुराना है, लेकिन मैं एक बहुत बड़ी समस्या का सामना कर रहा था - डेटा की एक बहुत बड़ी मात्रा को छोड़कर। कुछ संघर्ष के बाद, मैं परिणाम का एक पास में पेड़ का निर्माण करने में कामयाब रहा - संदर्भ का उपयोग करते हुए। यह कोड सुंदर नहीं है, लेकिन यह काम करता है और यह काफी तेज काम करता है। यह गैर-पुनरावर्ती है - अर्थात, परिणाम पर केवल एक पास है और फिर array_filterअंत में एक है:

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD);
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id");
$elems = array();

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
    $row['children'] = array();
    $vn = "row" . $row['n_id'];
    ${$vn} = $row;
    if(!is_null($row['n_parent_id'])) {
        $vp = "parent" . $row['n_parent_id'];
        if(isset($data[$row['n_parent_id']])) {
            ${$vp} = $data[$row['n_parent_id']];
        }
        else {
            ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array());
            $data[$row['n_parent_id']] = &${$vp};
        }
        ${$vp}['children'][] = &${$vn};
        $data[$row['n_parent_id']] = ${$vp};
    }
    $data[$row['n_id']] = &${$vn};
}
$dbs->closeCursor();

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); });
print_r($result);

जब इस डेटा को निष्पादित किया जाता है:

mysql> select * from test_table;
+------+-------------+
| n_id | n_parent_id |
+------+-------------+
|    1 |        NULL |
|    2 |        NULL |
|    3 |           1 |
|    4 |           1 |
|    5 |           2 |
|    6 |           2 |
|    7 |           5 |
|    8 |           5 |
+------+-------------+

अंतिम print_rइस आउटपुट का उत्पादन करता है:

Array
(
    [1] => Array
        (
            [n_id] => 1
            [n_parent_id] => 
            [children] => Array
                (
                    [3] => Array
                        (
                            [n_id] => 3
                            [n_parent_id] => 1
                            [children] => Array
                                (
                                )

                        )

                    [4] => Array
                        (
                            [n_id] => 4
                            [n_parent_id] => 1
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [2] => Array
        (
            [n_id] => 2
            [n_parent_id] => 
            [children] => Array
                (
                    [5] => Array
                        (
                            [n_id] => 5
                            [n_parent_id] => 2
                            [children] => Array
                                (
                                    [7] => Array
                                        (
                                            [n_id] => 7
                                            [n_parent_id] => 5
                                            [children] => Array
                                                (
                                                )

                                        )

                                    [8] => Array
                                        (
                                            [n_id] => 8
                                            [n_parent_id] => 5
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                    [6] => Array
                        (
                            [n_id] => 6
                            [n_parent_id] => 2
                            [children] => Array
                                (
                                )

                        )

                )

        )

)

जो बिल्कुल वही है जिसकी मुझे तलाश थी।


जबकि समाधान स्मार्ट है, लेकिन इस कोड में बग है, इसने मुझे अलग-अलग स्थितियों पर अलग-अलग परिणाम दिए
Mohammadhzp

@ मोहम्मदहप मैं पिछले साल के लिए उत्पादन में इस समाधान का उपयोग कर रहा हूं और इसके साथ कोई समस्या नहीं थी। यदि आपका डेटा अलग है, तो आपको अलग परिणाम मिलेंगे :)
Aleks G

@AleksG: मैंने टिप्पणी से पहले आपके उत्तर को बढ़ा दिया है, मुझे array_filter कॉलबैक में isset ($ elem ['children']) जोड़ना था, कुछ इस तरह से रिटर्न isset ($ elem ['children'] && is_null ($ elem [') है। n_parent_id ']); हो यह सही काम करने के लिए
Mohammadhzp

@Mohammadhzp आपके परिवर्तन के साथ, कोड काम नहीं करेगा अगर किसी शीर्ष-स्तरीय तत्व के कोई बच्चे नहीं हैं - यह पूरी तरह से सरणी से हटा दिया जाएगा।
एलेक्स जी

@AleksG, php के नवीनतम संस्करण का उपयोग करने के साथ, जो मेरे लिए परिणाम का नहीं होता है, अगर मैं isset ($ elem ['children') को हटा देता हूं और शीर्ष-स्तरीय तत्व में बच्चे होते हैं, तो मुझे उस शीर्ष-स्तर के दो भिन्न सरणी मिलेंगे तत्व (बच्चों के साथ एक और बिना एक) "मैं सिर्फ (2 मिनट पहले और फिर से परीक्षण किया isset के बिना) मैं अलग (गलत) परिणाम प्राप्त"
Mohammadhzp

1

अन्य उत्तरों से प्रेरणा लेते हुए, मैं प्रत्येक स्तर पर समूहीकरण कुंजी प्राप्त करने के लिए कस्टम फ़ंक्शंस की सूची का उपयोग करके, रिकर्सिकली (किसी भी मनमाने ढंग से गहराई तक) एरे के समूह को वर्गीकृत करने के लिए अपने स्वयं के संस्करण के साथ आया ।

यहां मूल अधिक जटिल संस्करण का एक सरलीकृत संस्करण है (ट्वीकिंग नॉब के लिए अधिक पैरामस के साथ)। ध्यान दें कि यह व्यक्तिगत स्तरों पर समूहन करने के लिए एक उप-पाठ के रूप में एक साधारण पुनरावृत्त कार्यgroupByFn को नियोजित करता है।

/**
 * - Groups a (non-associative) array items recursively, essentially converting it into a nested
 *   tree or JSON like structure. Inspiration taken from: https://stackoverflow.com/a/8587437/3679900
 * OR
 * - Converts an (non-associative) array of items into a multi-dimensional array by using series
 *   of callables $key_retrievers and recursion
 *
 * - This function is an extension to above 'groupByFn', which also groups array but only till 1 (depth) level
 *   (whereas this one does it till any number of depth levels by using recursion)
 * - Check unit-tests to understand further
 * @param array $data Array[mixed] (non-associative) array of items that has to be grouped / converted to
 *                    multi-dimensional array
 * @param array $key_retrievers Array[Callable[[mixed], int|string]]
 *                    - A list of functions applied to item one-by-one, to determine which
 *                    (key) bucket an item goes into at different levels
 *                    OR
 *                    - A list of callables each of which takes an item or input array as input and returns an int
 *                    or string which is to be used as a (grouping) key for generating multi-dimensional array.
 * @return array A nested assoc-array / multi-dimensional array generated by 'grouping' items of
 *               input $data array at different levels by application of $key_retrievers on them (one-by-one)
 */
public static function groupByFnRecursive(
    array $data,
    array $key_retrievers
): array {
    // in following expression we are checking for array-length = 0 (and not nullability)
    // why empty is better than count($arr) == 0 https://stackoverflow.com/a/2216159/3679900
    if (empty($data)) {
        // edge-case: if the input $data array is empty, return it unmodified (no need to check for other args)
        return $data;

        // in following expression we are checking for array-length = 0 (and not nullability)
        // why empty is better than count($arr) == 0 https://stackoverflow.com/a/2216159/3679900
    } elseif (empty($key_retrievers)) {
        // base-case of recursion: when all 'grouping' / 'nesting' into multi-dimensional array has been done,
        return $data;
    } else {
        // group the array by 1st key_retriever
        $grouped_data = self::groupByFn($data, $key_retrievers[0]);
        // remove 1st key_retriever from list
        array_shift($key_retrievers);

        // and then recurse into further levels
        // note that here we are able to use array_map (and need not use array_walk) because array_map can preserve
        // keys as told here:
        // https://www.php.net/manual/en/function.array-map.php#refsect1-function.array-map-returnvalues
        return array_map(
            static function (array $item) use ($key_retrievers): array {
                return self::groupByFnRecursive($item, $key_retrievers);
            },
            $grouped_data
        );
    }
}

यूनिट-परीक्षणों के साथ सरणी उपयोगिता कार्यों के बड़े संग्रह के लिए जिस्ट चेकआउट करें


-1

Mysql परिणाम को सरणी में लाने के लिए php का उपयोग करना संभव है और फिर इसका उपयोग करें।

$categoryArr = Array();
while($categoryRow = mysql_fetch_array($category_query_result)){
    $categoryArr[] = array('parentid'=>$categoryRow['parent_id'],
            'id'=>$categoryRow['id']);
   }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.