Search code examples
phpxmlsimplexml

How to extract and Sort a Multidimensional Array data with PHP from a XML file (using simpleXML)


Hope you can help me with my issue...

I have this XML file, which I'm getting the info with the function simplexml_load_file():

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <Xiami>
    <ArtistID>179</ArtistID>
    <ArtistName>Band 1</ArtistName>
    <XiamiID>9dl4gN1c745</XiamiID>
    <Streaming>246</Streaming>
    <Fans>84</Fans>
    <TotalComments>1992</TotalComments>
      <AllSongs>
        <Songs>
          <SongID>8ILroS998dc</SongID>
          <SongName>Song 1</SongName>
          <SongComments>9223</SongComments>
        </Songs>
        <Songs>
          <SongID>8ILLD61a351</SongID>
          <SongName>Song 2</SongName>
          <SongComments>7221</SongComments>
        </Songs>
        <Songs>
          <SongID>mTnf0L5d6b9</SongID>
          <SongName>Song 3</SongName>
          <SongComments>21212</SongComments>
        </Songs>
        <Songs>
          <SongID>xOd2YIc7f69</SongID>
          <SongName>Song 4</SongName>
          <SongComments>422</SongComments>
        </Songs>
        <Songs>
          <SongID>mTmg866314c</SongID>
          <SongName>Song 5</SongName>
          <SongComments>81211</SongComments>
        </Songs>
    </AllSongs>
  </Xiami>
</rss>

This is the code that I'm using in order to get the data:

<?php
    foreach($xml->children() as $Artist) {
        $ArtistName     = $Artist->ArtistName;
        $Streaming      = $Artist->Streaming;
        $Fans           = $Artist->Fans;
        $SongsArrays    = $Artist->AllSongs;
        $SongsCount     = $Artist->AllSongs->Song->count();
    }
?>

If I print print_r($SongsArrays), I'm getting the following array:

SimpleXMLElement Object
(
    [Song] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [SongID] => 8ILroS998dc
                    [SongName] => Song 1
                    [SongComments] => 9223
                )
            [1] => SimpleXMLElement Object
                (
                    [SongID] => 8ILLD61a351
                    [SongName] => Song 2
                    [SongComments] => 7221
                )
            [2] => SimpleXMLElement Object
                (
                    [SongID] => mTnf0L5d6b9
                    [SongName] => Song 3
                    [SongComments] => 21212
                )
            [3] => SimpleXMLElement Object
                (
                    [SongID] => xOd2YIc7f69
                    [SongName] => Song 4
                    [SongComments] => 422
                )
            [4] => SimpleXMLElement Object
                (
                    [SongID] => mTmg866314c
                    [SongName] => Song 5
                    [SongComments] => 81211
                )
        )
)

If I print the whole array, Imm getting this:

SimpleXMLElement Object
(
    [ArtistID] => 179
    [ArtistName] => Band 1
    [XiamiID] => 9dl4gN1c745
    [Streaming] => 246
    [Fans] => 84
    [AllSongs] => SimpleXMLElement Object
        (
            [Song] => Array
                (
                    [0] => SimpleXMLElement Object
                        (
                            [SongID] => 8ILroS998dc
                            [SongName] => Song 1
                            [SongComments] => 9223
                        )
                    [1] => SimpleXMLElement Object
                        (
                            [SongID] => 8ILLD61a351
                            [SongName] => Song 2
                            [SongComments] => 7221
                        )
                    [2] => SimpleXMLElement Object
                        (
                            [SongID] => mTnf0L5d6b9
                            [SongName] => Song 3
                            [SongComments] => 21212
                        )
                    [3] => SimpleXMLElement Object
                        (
                            [SongID] => xOd2YIc7f69
                            [SongName] => Song 4
                            [SongComments] => 422
                        )
                    [4] => SimpleXMLElement Object
                        (
                            [SongID] => mTmg866314c
                            [SongName] => Song 5
                            [SongComments] => 81211
                        )
                )
        )
)

The question is:

  1. How can I extract the first 3 songs with more comments (get the comments, name and ID)
  2. How to sum all the comments together?...

I've tried almost everything what google and other forums reccomends, but cannot find how to get this data without write a huge code (separating each array and creating a lot of loops)... is there any faster and easy way to do it?


Solution

  • You could use this function to extract the most popular songs for an artist. It uses xpath to get the Songs from AllSongs and then usort to sort that list by SongComments descending, finally using array_slice to return only the 3 most popular songs:

    function popular_songs($Artist) {
        $Songs = $Artist->xpath('//Songs');
        usort($Songs, function ($a, $b) { return $b->SongComments - $a->SongComments; });
        return array_slice($Songs, 0, 3);
    }
    

    Inside your loop you would call it as:

    $PopularSongs   = popular_songs($Artist);
    

    If you then print_r($PopularSongs) you will get (for your sample data)

    Array
    (
        [0] => SimpleXMLElement Object
            (
                [SongID] => mTmg866314c
                [SongName] => Song 5
                [SongComments] => 81211
            )
        [1] => SimpleXMLElement Object
            (
                [SongID] => mTnf0L5d6b9
                [SongName] => Song 3
                [SongComments] => 21212
            )
        [2] => SimpleXMLElement Object
            (
                [SongID] => 8ILroS998dc
                [SongName] => Song 1
                [SongComments] => 9223
            )
    )
    

    To get the total comments per artist, you can use this code, which finds all the SongComment elements, converts them to integers and sums them:

    $TotalComments  = array_reduce($Artist->xpath('//SongComments'), function ($c, $v) { return $c + $v; }, 0);
    

    For your sample data, this gives 119289.

    Demo on 3v4l.org