views:

31

answers:

2

I've been trying to get some display logic to behave and stay where it belongs and the code has turned into an interesting little problem that'd be nice to make a general solution for.

Bear with me, it looks like a wall of text but I've tried to format it nicely with simple example data so it should be understandable after a quick skim through.

If for some reason this whole thing is a terrible idea, I need telling before I create an affront to the gods.

Starting with data like this, for example:

$data = array(

    array(
        'name' => 'Dave',
        'age'  => '21',
        'city' => 'New York',
    ),
    array(
        'name' => 'Mike',
        'age'  => '19',
        'city' => 'Chicago',
    ),
    array(
        'name' => 'John',
        'age'  => '21',
        'city' => 'Chicago',
    ),
    array(
        'name' => 'Matt',
        'age'  => '19',
        'city' => 'New York',
    ),
    array(
        'name' => 'Luke',
        'age'  => '21',
        'city' => 'New York',
    ),

);

With an array of key names by which to group the data, such as

$groups = array('city', 'age);

The data then becomes:

$data = array(

    'New York' => array(
        '21' => array(
            array(
                'name' => 'Dave',
                'age'  => '21',
                'city' => 'New York',
            ),
            array(
                'name' => 'Luke',
                'age'  => '21',
                'city' => 'New York',
            ),
        ),
        '19' => array(
            array(
                'name' => 'Matt',
                'age'  => '19',
                'city' => 'New York',
            ),
        ),
    ),
    'Chicago' => array(
        '19' => array(
            array(
                'name' => 'Mike',
                'age'  => '19',
                'city' => 'Chicago',
            ),
        ),
        '21' => array(
            array(
                'name' => 'John',
                'age'  => '21',
                'city' => 'Chicago',
            ),
        ),
    ),
);

When I say "general solution", I mean I'm trying to make something that can group things to any nesting level depending on how many of the key names you ask it to group by.

It feels like the sort of problem that I could solve instantly if I just knew some random esoteric bit of PHP syntax. Any suggestions? I'll try to update this is if I figure it out in the meantime.

+3  A: 
function group_array($arr, $fields) {

    if(empty($fields) || !is_array($fields)) {
        return $arr;
    }

    $newarr = array(); // so that we always return an array
    $field = array_shift($fields);

    foreach($arr as $val) {
        $newarr[$val[$field]][] = $val;
    }

    foreach(array_keys($newarr) as $key) {
        // Since we shifted one field off before, this groups by the remaining
        $newarr[$key] = group_array($newarr[$key], $fields);
    }

    return $newarr;
}
Amber
Damn man, that's exactly what I'm talking about. I saw your little ninja edit to stop the infinite recursion too, nice one! Literally the only thing that needed changing to make this run was for => foreach and it was done. Which means you didn't even test this to catch the syntax error and the actual complex part was right first time. I am in awe.
Henry
Heh, I've been working with Python lately so I forgot to make them `foreach`'s. Fixed now.
Amber
A: 

I was originally trying to sort arrays of objects so since I was gonna write it anyway, here's one that works on objects, even if it is a slightly hacky idea

function group_objects($array, $fields)
{
    if (empty($fields) || !is_array($fields)) {
        return $array;
    }

    $newArray = array();
    $field    = array_shift($fields);

    foreach ($array as $object) {
        $key = call_user_func(array($object, 'get' . ucwords($field)));
        $newArray[$key][] = $object;
    }

    foreach (array_keys($newArray) as $key) {
        $newArray[$key] = group_objects($newArray[$key], $fields);
    }

    return $newArray;
}
Henry