Simon
Simon

Reputation: 171

Consolidate multidimensional array data by deep array value and ensure default-keyed subarrays within each group

I have an array that I'd like to restructure. I want to group items by turn. I can figure out how to extract data from the array using foreach($arr['history'] as $obj) my issue is with populating a new array using a loop.

Currently it looks like this:

Array ( 
[history] => Array ( 
    [id] => 23452435 
    [legend] => Array ( 

        [0] => Array ( 
            [player] => me 
            [turn] => 1 
            [card] => Array ( 
                [name] => foo 
            ) 
        ) 

        [1] => Array ( 
            [player] => me 
            [turn] => 1 
            [card] => Array ( 
                [name] => bar
            ) 
        ) 

        [2] => Array ( 
            [player] => opponent 
            [turn] => 1
            [card] => Array (
                [name] => derp 
            ) 
        ) 

        [3] => Array ( 
            [player] => opponent 
            [turn] => 2 
            [card] => Array ( 
                [name] => hoo
            ) 
        ) 
    ) 
))

I want it to look like the following, but I can't figure out how to automatically create and populate this structure. This is an array with a sub-array for each turn, containing an array for me and opponent

Array (
[0] => Array (
    [me] => Array (
        [0] => foo
        [1] => bar
    )
    [opponent] = Array (
        [0] => derp
    )

)
[1] => Array (
    [me] => Array ()
    [opponent] => Array (
        [0] => hoo
    )
))

Upvotes: 1

Views: 1802

Answers (3)

Naveed Ahmed
Naveed Ahmed

Reputation: 166

Try this script which subtracts 1 from the turn value to declare the first level key and then the player value as the second level key, then pushes the card value as a new subarray element.

$result = [];
foreach ($data['history']['legend'] as $list) {
    $result[$list['turn'] - 1][$list['player']][] = $list['card']['name'];
}

Fiddle it! http://ideone.com/BtKOKJ

Upvotes: 1

mickmackusa
mickmackusa

Reputation: 47894

I'm demonstrate a solution implementing array destructuring syntax in a foreach() for convenience value access. Inside the loop body, a reference variable will be maintained to ensure that every unique turn contains a me and an opponent subarray. Pushing data into references avoids subtracting from the turn value to dictate keys (subtraction might produce non-indexed keys depending on turn values).

Code: (Demo)

$result = [];
foreach ($data['history']['legend'] as ['player' => $p, 'turn' => $t, 'card' => ['name' => $cn]]) {
    if (!isset($ref[$t])) {
        $ref[$t] = ['me' => [], 'opponent' => []];
        $result[] =& $ref[$t];
    }
    $ref[$t][$p][] = $cn;
}
var_export($result);

@Naveed's answer does not ensure that each group has a me and an opponent subarray.

@michaJlS's answer doesn't actually work at all when fed the asker's data.

Upvotes: 0

michaJlS
michaJlS

Reputation: 2500

You can just start adding data to the new array. PHP is extremely forgiving.

$historyByTurns = array();
foreach ($arr['history'] as $historyItem) {
    foreach ($historyItem['legend'] as $legendItem) {
        $turn = $legendItem['turn'];
        $player = $legendItem['player'];
        if (!array_key_exists($turn, $historyByTurns)) {
            $historyByTurns[$turn] = array();
        }
        if (!array_key_exists($player, $historyByTurns[$turn])) {
            $historyByTurns[$turn][$player] = array();
        }
        foreach ($legendItem as $card) {
            $historyByTurns[$turn][$player][] = $card['name'];
        }
    }
}

You will have to test it, as I have no way to do that ATM.

Upvotes: 1

Related Questions