1

Hoping I am not opening a duplicate question but I didn't see this type of question being asked or the answers I saw didn't seem to work with my dataset (but I'm very new at this).

I am hoping to sort the following data set (array of arrays) based on another array of strings.

The code is running and it is only running the generateHtml function for strings that exist in the $ntbooks array which is by design. What I need to solve is how to perform a custom sort using the $ntbooks as the order. Right now, as the code runs and the content is parsed from content.php, the order the data is populated into the menu is the order in which it is seen (top to bottom of array)--I would like to sort the data so that Matthew is written out, then Mark, then Luke, etc.

This is a sample of my data but there are over 250 arrays (contentids) in it and I would like to avoid restructuring or reorganizing my dataset. When all is said and done, the generateHTML should start with matthew, mark, luke, and work its way through the list.

Thank you in advance for your help!

Dynamically Generated HTML Menu (menu.php):

                    include('content.php');

                    $main = array();

                    foreach($articles as $article)
                    {
                        foreach(explode("; ", $article["verse"]) as $verseData)
                        {

                            $verse = explode(" ", $verseData);

                            $book = $verse[0];
                            $verse = explode(":", $verse[1]);
                            $chapter = $verse[0];

                            if(empty($main[$book]))
                                $main[$book] = array();

                            if(empty($main[$book][$chapter]))
                                $main[$book][$chapter] = array();

                            if(empty($main[$book][$chapter][$verse[1]]))
                                $main[$book][$chapter][$verse[1]] = array();

                            $main[$book][$chapter][$verse[1]] = array
                            (
                                "full" => $article["full"]
                            );
                        }
                    }                       

                    $ntbooks = array("Matthew", "Mark", "Luke", "John", "Acts", "Romans", "1Corinthians", "2Corinthians", "Galatians", "Ephesians", "Philippians", "Colossians", "1Thessalonians", "2Thessalonians", "1Timothy", "2Timothy", "Titus", "Philemon", "Hebrews", "James", "1Peter", "2Peter", "1John", "2John", "3John", "Jude", "Revelation");


                    foreach($main as $book => $data)
                    {

                        if (in_array($book, $ntbooks)) {

                            generateHtml($book, $data);

                        }

                    }

                    function generateHtml($book, $data)
                    {
                        echo "<!-- ". $book ." -->\n";
                        echo "<div class=\"submenu\">\n";
                        echo "<a href=\"#\">". $book ."</a>\n";
                        echo "<!-- Level 2 menu -->\n";
                        echo "<div>\n";
                        echo "<div>\n";

                        foreach($data as $chapter => $verses)
                        {
                            echo "<div class=\"submenu\">\n";
                            echo "<a href=\"#\"">Chapter ". $chapter ."</a>\n";
                            echo "<!-- Level 3 menu -->\n";
                            echo "<div>\n";
                            echo "<div>\n";

                            foreach($verses as $verse => $value)
                            {
                                echo "<a href=\"#\"">Verse ". $verse ."</a>\n";
                            }

                            echo "</div>\n";
                            echo "</div>\n";
                            echo "</div>\n";

                        }


                    }

PHP Array of Arrays (content.php):

$articles = array(

          array(
          'contentid' => '0',
          'full' => 'Item1 by Artist Name1 (Matthew 4)',
          'verse' => 'Matthew 4:18-22',
          'commentary' => ''),

          array(
          'contentid' => '1',
          'full' => 'Item2 by Artist Name2 (Luke 15)',
          'verse' => 'Luke 15:11-14; Luke 15:20-21',
          'commentary' => ''),           

          array(
          'contentid' => '2',
          'full' => 'Item3 by Artist Name3 (John 3)',
          'verse' => 'John 3:1-9',
                      'commentary' => ''),    

          array(
          'contentid' => '3',
          'full' => 'Item4 by Artist Name4 (John 8)',
          'verse' => 'John 8:1-15A',
          'commentary' => ''),

          array(
          'contentid' => '4',
          'full' => 'Item5 by Artist Name5 (Matthew 27; Luke 23; John 19)',
          'verse' => 'Matthew 27:20-23A; Luke 23:20-25A; John 19:2-3A',
          'commentary' => 'Here '));
Kurt W
  • 321
  • 2
  • 15
  • 1
    Please make sure you show us your current array and your expected output array – Rizier123 May 16 '15 at 11:03
  • Since the $articles array has one or more verses, I need to basically break it out so that contentID4 (for example), becomes 'Matthew 27:20-23A' and 'Luke 23:20-25A' and 'John 19:2-3A'... as opposed to all one string. Then, as the array is processed all books/chapters/verses are seen, and $ntbooks is used for the sort. In short, all Matthew entries should be processed, then Mark, then Luke, etc. I suspect that the verse will be broken down from 'Matthew 27:20-23A' to simply 'Matthew' while retaining the chapter/verse for later. Then after the sort is done, the verse will be added back.Thx! – Kurt W May 16 '15 at 21:06
  • @Rizier123, please let me know if you need more information. Thanks! – Kurt W May 18 '15 at 05:54

2 Answers2

0

Use usort.

From: http://php.net/manual/en/function.usort.php

bool usort ( array &$array , callable $value_compare_func ).

Basically, you call usort with the array you would like to sort and a function that would indicate the sort order (compare the two objects and determines which one is "greater").

Since the book is in the verse object, you can do something like this:

function sortCompare($obj1, $obj2) { $book1 = explode($obj1['verse'])[0]; $book2 = explode($obj2['verse'])[0]; return indexOf($book1, $ntbooks) - indexOf($book2, $ntbooks); }

At which point you can call usort($articles, "sortCompare");.

The sortCompare function above might need tweaking but it's a good start.

ajacian81
  • 7,419
  • 9
  • 51
  • 64
  • what version of PHP is this for? I'm getting 1 warning & 1 error: explode() expects at least 2 parameters, 1 given ---- Fatal error: Call to undefined function indexOf(). I can assume that I just need to pass a delimiter for the explodes, but indexOf() doesn't seem to have an easy workaround. Do I need to import a lib before using this? – Kurt W May 16 '15 at 19:48
  • I swapped out indexOf for the PHP equivalent but I am still struggling. Are you able to assist? – Kurt W May 17 '15 at 23:43
0

You need to sort using a custom comparison function, where the values being compared will be the keys from the reference array that correspond to the values from the data array.

So if:

$order = ['Matthew', 'Mark', 'Luke'];

and your data array's verse is guaranteed to be one of the above values (in your example this is not true!) then you could do it with

$comparison = function($a, $b) use ($order) {
    return array_search($a['verse'], $order) - array_search($b['verse'], $order);
};

usort($data, $comparison);

However, it's not as simple in your case because verse is not as convenient as that. You will need to somehow extract the value of interest out of it, an exercise which I will not attempt to solve because there are too many unknowns (e.g. how is 'Matthew 27:20-23A; Luke 23:20-25A; John 19:2-3A' supposed to be sorted?).

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Hi @Jon, in answer to your question, Matthew 27:20-23A; Luke 23:20-25A; John 19:2-3A' would simply be sorted with Matthew first, then Luke, then John. This order happens to already match the desired order, however if the dataset was 'Romans 12:1-4; Luke 2:1-10; James 5:1, Matthew 4:1-3' for example, the revised sort would show Matthew 4, then Luke 2, then Romans 12, then finally James 5. The data I provided was a sample but verse IS guaranteed to have a match in $ntbooks within $main[$book]. Does that make sense? – Kurt W May 16 '15 at 18:08
  • Spent a couple of hours trying this... but I clearly don't know what I'm doing... @Jon, how would you tackle processing all of the verses? They are in the format from my array of arrays listed above... but of course there are many more. My actual array has ~40 potential books--I probably should have provided more varying data but I think you get the idea. Any help is greatly appreciated!! If I can sort it all together without breaking it out ('Matthew 27:20-23A' vs. 'Matthew', '27', '20-23A') and sort book on $ntbooks and the other fields numerically, that would be amazing. Thanks Jon! – Kurt W May 17 '15 at 02:20