Как я могу преобразовать этот массив M-N mysql в этот другой массив в PHP? (примеры внутри) - PullRequest
1 голос
/ 03 января 2012

Что ж, довольно часто можно получить такой массив в отношениях MN в mysql:

[0]
'book_id' => 1
'title' => 'title'
'author_id' => 1
'author_name' => 'name1'

[1]
'book_id' => 1
'title' => 'title'
'author_id' => 2
'author_name' => 'name2'

Итак, существует элегантный / простой способ преобразования этого вида или массивов в нечто подобное?

'book_id' => 1
'title' => 'title'
'authors' => array( 0 => array( 'author_id' => 1, 'author_name' => 'name1' ),
1 => array( 'author_id' => 2, 'author_name' => 'name2' ) )

Я не нахожу ни одного скрипта или комбинации функций, которые бы сделали это ... и довольно часто, может быть, я не правильно искал ... не знаю, как вызвать проблему ...

Есть идеи или опыт?

Спасибо:)

PS: я не хочу использовать GROUP BY + GROUP CONCACT в MySQL, я нашел это довольно уродливое решение...

РЕДАКТИРОВАТЬ: Я работаю над чем-то общим, не только для решения этой конкретной проблемы ..

Ответы [ 5 ]

2 голосов
/ 03 января 2012

Общее решение для нескольких подключей:

$rows = collapse_rows($rows, 'book_id', array(
        'author_id' => array('author_name'),
    ));

function collapse_rows($rows, $key, $subkeys){

    # make a map of all fields we don't perform a simple copy on
    $skip_fields = array();
    foreach ($subkeys as $k => $v){
        $skip_fields[$k] = 1;
        foreach ($v as $v2) $skip_fields[$v2] = 1;
    }

    # now build our output
    $out = array();
    foreach ($rows as $row){
        $row2 =& $out[$row[$key]];

        # simple fields first
        foreach ($row as $k => $v){
            if (!$skip_fields[$k])){
                $row2[$k] = $v;
            }
        }

        # now subkeys
        foreach ($subkeys as $k => $v){
            $sub_row = array($k => $row[$k]);
            foreach ($v as $v2) $sub_row[$v2] = $row[$v2];
            $row2[$k][$sub_row[$k]] = $sub_row;
        }
    }

    return $out;
}

Это позволяет передавать первичный ключ и хэш вспомогательных ключей и полей для агрегирования.

2 голосов
/ 03 января 2012

Я бы сделал JOIN в SQL и постобработал бы результаты во вложенный массив в коде PHP.

$bookkeys = array_flip(array("book_id", "title"));
$authorkeys = array_flip(array("author_id", "author_name"));

$stmt = $dbh->query("...");

$books = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  if (!array_key_exists($row["book_id"], $books)) {
    $books[ $row["book_id"] ] = array_intersect_key($row, $bookkeys);
  }
  $books[ $row["book_id"] ]["authors"][] = array_intersect_key($row, $authorkeys);
}

Если вы хотите, чтобы конечный массив был порядковым, а не указанным в key_id, вы можете преобразовать его:

$books = array_values($books);
1 голос
/ 03 января 2012

Ну, у меня есть «решение», основанное на предложении Билла Карвина:

$key    = 'authors';
$output = array();
if ( is_array( $elements ) )
{
    $all_keys           = array_keys( $elements[0] );
    $conflicted_keys    = call_user_func_array( 'array_diff_assoc', $elements );
    $conflicted_keys    = array_keys( $conflicted_keys );
    $good_keys          = array_diff( $all_keys, $conflicted_keys );

    $conflicted_keys_fliped     = array_flip( $conflicted_keys );
    $good_keys_fliped           = array_flip( $good_keys );

    foreach ( $elements as $row )
    {
        if ( !array_key_exists( $row[$good_keys[0]], $output ) ) {
            $output[ $row[$good_keys[0]] ]          = array_intersect_key($row, $good_keys_fliped);
        }
        $output[ $row[$good_keys[0]] ][ $key ][]    = array_intersect_key( $row, $conflicted_keys_fliped );
    }
}

$output     = array_values($output);
var_dump($output);

Не знаю, является ли это наиболее эффективным / правильным. С другой стороны, это не будет работать, если естьболее одного типа конфликта ... исправления будут приняты :))

спасибо всем!

0 голосов
/ 04 января 2012

Ну, это функция, которую я, наконец, буду использовать, она полностью основана на идее Cal, но пытается использовать нативные функции, как сказал Билл Карвин:

protected function collapse_rows( $rows, $key, $subkeys ) {
if ( is_array( $rows ) )
{
    // make a map of all fields we don't perform a simple copy on
    $skip_fields = array();
    foreach( $subkeys as $sub ) {
        $skip_fields = array_merge( $skip_fields, array_reverse( $sub ) );
    }        
    $skip_fields = array_flip( $skip_fields );

    // now build our output
    $out = array();
    foreach ( $rows as $row ) {

        // simple fields first
        if ( !array_key_exists( $row[$key], $out ) ) {
            $out[ $row[$key] ]  = array_diff_key( $row, $skip_fields );
        }

        // now subkeys
        foreach ( $subkeys as $k => $v ) {
            $value      = array_intersect_key( $row, array_flip( $subkeys[$k] ) );
            if ( !empty( $value ) ) {
                $out[ $row[$key] ][ $k ][]  = $value;   
            }
        } 
    }

    return array_values( $out );
}
return $rows;

}

После этого я не думаю, что у него слишком хорошие показатели ... Интересно, как другие люди решают такие проблемы ...

Что угодно, спасибо обоим!

0 голосов
/ 03 января 2012

Будет работать что-то простое:

$out = array();
foreach ($rows as $row){
    $out[$row['book_id']]['book_id'] = $row['book_id'];
    $out[$row['book_id']]['title'] = $row['title'];
    $out[$row['book_id']]['authors'][$row['author_id']] = array(
        'author_id' => $row['author_id'],
        'author_name'   => $row['author_name'],
    );
}

Строки вводятся по идентификатору книги, а затем по идентификатору автора, поэтому у нас есть только одна выходная строка на книгу, а затем на автора внутри этой строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...