1

I have the following array called $zoo

Array
(
[0] => &dog&
[1] => *1*
[2] => one
[3] => &cat&
[4] => *2*
[5] => two
[6] => &mouse&
[7] => *3*
[8] => three
[9] => &dog&
[10] => *4*
[11] => four
)

and i need the following result:

Array
(
[&dog&] => Array
    (
        [0] => Array
            (
                [0] => *1*
                [1] => one
            )
        [1] => Array
            (
                [0] => *4*
                [1] => four
            )
    )
[&cat&] => Array
    (
        [0] => Array
            (
                [0] => *2*
                [1] => two
            )
    )
[&mouse&] => Array
    (
        [0] => Array
            (
                [0] => *3*
                [1] => three
            )
    )
)

This is what I have come up with but the problem is that for [&dog&] he gives me only the last value (namely 4, four) and not the first value (1, one)

$animals=array();
for ($i = 0; $i < count($zoo); $i++)
{
if($zoo[$i][0] === "&")
    {
    $name=$zoo[$i];
    if (isset($name))
        $animals[$name] = array($liste);
    else
        $animals[$name] = array($liste);
    $liste="";
    }
if($zoo[$i][0] !== "&")
    {        
    $number = $zoo[$i];
    $liste[] = $number;
    $animals[$name] = array($liste);
    }
}
print_r($animals);

this results in

Array
(
[&dog&] => Array
    (
        [0] => Array
            (
                [0] => *4*
                [1] => four
            )
    )
[&cat&] => Array
    (
        [0] => Array
            (
                [0] => *2*
                [1] => two
            )
    )
[&mouse&] => Array
    (
        [0] => Array
            (
                [0] => *3*
                [1] => three
            )
    )
)

can anyone point me in the good direction?

Preys
  • 103
  • 7
  • My solution does work as intended. . . let me know if it isn't enough for you. – Levi Morrison Oct 13 '11 at 08:24
  • @levi : this is exactly what I need. Let me add a problem with the following array: Array ( [0] => *1* [1] => &dog& [2] => one [3] => *2* [4] => &cat& [5] => two [6] => *3* [7] => &mouse& [8] => three [9] => *4* [10] => &dog& [11] => four ) and I need the SAME RESULT. so now the word between & still has to be the key but the number between * that precedes the word between & should be the first element of the array in the value. – Preys Oct 13 '11 at 13:17

2 Answers2

2
$animals = array();

$c = count($zoo)/3;
for ($i = 0 ; $i < $c ; $i++)
{
    $species = array_shift($zoo);
    $number = array_shift($zoo);
    $number_text = array_shift($zoo);

    $animals[$species] []= array($number, $number_text);
}

If you want the start of a new animal be triggered by the "&" in the text, there are several solutions. Here's mine:

$animals = array();
unset($current_animal);
foreach ($zoo as $text)
{
    if ($text{0} == '&')
    {
        // Insert an element for the new animal and get a reference to it
        $animals[$text] []= array();
        end($animals[$text]);
        $current_animal =& $animals[$text][key($animals[$text])];
    }
    else
        $current_animal []= $text;
}
unset($current_animal);
AndreKR
  • 32,613
  • 18
  • 106
  • 168
  • Brittle, what if he has more than two values associated with each new key? – Levi Morrison Oct 12 '11 at 21:22
  • @herbert And where does it say that? Sure, he asked with that example, but it's clear from his implementation that he was searching based on `&`. – Levi Morrison Oct 12 '11 at 21:28
  • 1
    @Levi: It's clear from his _question_ that he has an array in one form and wants it in another form. The OP never said it was an example. If it is then the OP needs to specify that. – Herbert Oct 12 '11 at 21:34
  • @Herbert, I'm not going to argue it, but that looks a lot like sample data. . . – Levi Morrison Oct 12 '11 at 21:39
  • @Levi: I agree to disagree since the OP hasn't chimed in one way or the other. – Herbert Oct 12 '11 at 21:51
  • @Levi and Herbert: indeed I forgot to add that in the result the key must be the word between '&', sorry – Preys Oct 13 '11 at 05:31
1

A solution that works for an array that has any number of indices that should grouped. It simply searches for a & prefix and adds the new values to the animals entry.

    $zooAmp = array(
            '&dog&',
            '*1*',
            'one',
            '&cat&',
            '*2*',
            'two',
            '&mouse&',
            '*3*',
            'three',
            '&dog&',
            '*4*',
            'four'
    );

    $zooStar = array(
            '*1*',
            '&dog&',
            'one',
            '*2*',
            '&cat&',
            'two',
            '*3*',
            '&mouse&',
            'three',
            '*4*',
            '&dog&',
            'four'
    );

    function & refactor(array $unfactored) {
            $len = count($unfactored);
            $data = array();

            if ($len<3) {
                    return $data;
            }

            if ($unfactored[0][0]=='&') {
                    //this algorithm isn't too bad, just loop through and add the ones starting with
                    //'&' to the data, and write everything from that index down to the next '&'
                    //into the created index in data.

                    $i=0;
                    while ($i<$len) {
                            if (!array_key_exists($unfactored[$i], $data))
                                    $data[$unfactored[$i]] = array();

                            //save to $arr for easier reading and writing
                            $arr = &$data[$unfactored[$i]];

                            $index = count($arr);
                            $arr[$index] = array();

                            for ($c=$i+1; $c<$len && $unfactored[$c][0]!='&'; $c++) {
                                    $arr[$index][] = $unfactored[$c];
                            }


                            $i = $c;
                    }

            } elseif ($unfactored[0][0]=='*') {
                    //this algorithm is a bit harder, but not so bad since we've already done the
                    //basic algorithm above.  We just need to store the ones with a '*' and then
                    //add them back into the array after it's been created.

                    $i=0;
                    $unorganizedItem = NULL;

                    while ($i<$len) {
                            $key = $unfactored[$i];

                            if ($key[0]=='*') {
                                    $unorganizedItem = $key;
                                    $i++;
                            } elseif ($key[0]=='&') {
                                    if(!array_key_exists($key, $data))
                                            $data[$key] = array();

                                    //save to $arr for easier reading and writing
                                    $arr = &$data[$key];
                                    $index = count($arr);

                                    $arr[$index][] = $unorganizedItem;
                                    $unorganizedItem = null;

                                    for ($c=$i+1; $c<$len && $unfactored[$c][0]!='&'; $c++) {
                                            if ($unfactored[$c][0]=='*') {
                                                    $unorganizedItem = $unfactored[$c];
                                            } else {
                                                    $arr[$index][] = $unfactored[$c];
                                            }
                                    }

                                    $i = $c;

                            }
                    }

            }

            return $data;

    }

    print_r(refactor($zooAmp));
    print_r(refactor($zooStar));

Prints:

Array
(
    [&dog&] => Array
        (
            [0] => Array
                (
                    [0] => *1*
                    [1] => one
                )

            [1] => Array
                (
                    [0] => *4*
                    [1] => four
                )

        )

    [&cat&] => Array
        (
            [0] => Array
                (
                    [0] => *2*
                    [1] => two
                )

        )

    [&mouse&] => Array
        (
            [0] => Array
                (
                    [0] => *3*
                    [1] => three
                )

        )

)
Array
(
    [&dog&] => Array
        (
            [0] => Array
                (
                    [0] => *1*
                    [1] => one
                )

            [1] => Array
                (
                    [0] => *4*
                    [1] => four
                )

        )

    [&cat&] => Array
        (
            [0] => Array
                (
                    [0] => *2*
                    [1] => two
                )

        )

    [&mouse&] => Array
        (
            [0] => Array
                (
                    [0] => *3*
                    [1] => three
                )

        )

)
Levi Morrison
  • 19,116
  • 7
  • 65
  • 85
  • Good robust and general solution. +1 – Herbert Oct 12 '11 at 21:51
  • @levi : this is exactly what I need. Let me add a problem with the following array: Array ( [0] => *1* [1] => &dog& [2] => one [3] => *2* [4] => &cat& [5] => two [6] => *3* [7] => &mouse& [8] => three [9] => *4* [10] => &dog& [11] => four ) and I need the same result. so now the word between & still has to be the key but the word between * should be the first element of array in the value. – Preys Oct 13 '11 at 13:09
  • @Preys, does it need to be the same function? And will the two formats ever mix? – Levi Morrison Oct 13 '11 at 17:16
  • @Levi no, these are two independant functions. They will not mix. – Preys Oct 13 '11 at 17:53
  • @Preys, can we assume that each sub-array will have the same number of elements? Meaning, `&dog&[0]` will have the same number of elements as `&dog&[1]`? – Levi Morrison Oct 13 '11 at 18:17
  • @Levi I'm afraid not the number of elements following the word between & can be infinite and different but before the word between & there will be only one word – Preys Oct 13 '11 at 18:31
  • @Preys So, `array('&dog&','*1*', 'one', 'I', '&cat&', '*2*')` would be valid? – Levi Morrison Oct 13 '11 at 18:44
  • @Levi in the original array following the word between *(asterisk) there would be at least one word. the array should be composed of a word between &, followed by one word between '*' and then there can be as many words from one to endless, then again one word between &, one between * and then a series of words and so on. – Preys Oct 13 '11 at 19:08
  • @Preys and just to clarify, the order of `&` and `*` might be flopped, but it will be consistent for the whole array? – Levi Morrison Oct 13 '11 at 19:17
  • @Levi indeed my mistake, the word between * will ALWAYS come first and the word between & will come on second place, but it is the word between & that must serve as key in the result. – Preys Oct 13 '11 at 19:32
  • @Levi impressif, it works but it will take me some time to analyse how it works. Thanks (and for me, good night) – Preys Oct 13 '11 at 20:31
  • @Preys I'd appreciate it if you accepted this as the answer :) – Levi Morrison Oct 13 '11 at 20:35