Laravel のコレクションで特定の要素を key にする

カテゴリー
Laravel

やりたいこと

コレクションの要素をキーにする。ただし要素からは消したい。

例えば、id, name, emailみたいな構造のデータベースのテーブルがあるとします。

ここから複数行のレコードを取得し、下記のような感じで整形したいとします。

id1 => [name1, email1],
id2 => [name2, email2],
id3 => [name3, email3],

やり方

mapWithKeys() で。
Laravel 5.4以降のCollectionで使用可能なメソッドです。

テスト実装

例えば下記のように実装してみましょう。

$users_raw->mapWithKeys(function ($item)
{
    return [
        $item['id'] => [
            'name'  => $item['name'],
            'email' => $item['email'],
        ]
   ];
})->toArray();

テストコード全体

/tests/Unit/MapWithKeysTest.php
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Collection as Collection;
use App\User as User;
class ExampleTest extends TestCase
{
    public function testMapWithKeys()
    {
        echo "\n",'=== ',__FUNCTION__,' ===',"\n";
        // モデルファクトリーで定義したテストユーザーを作成
        $users_raw = factory(User::class, 3)->make();
        // DBにINSERTしていないため、手動でIDを追加
        foreach ($users_raw as $key => &$user) {
            $user->id = $key + 1;
        }
        // テストデータ表示
        echo "\n",'=== TEST_D\ATA ===',"\n";
        var_dump($users_raw);
        // テストの実装
        $users_result = $users_raw->mapWithKeys(function ($item)
        {
            return [
                $item['id'] => [
                    'name'  => $item['name'],
                    'email' => $item['email'],
                ]
            ];
        })->toArray();
        // テスト結果
        echo "\n",'=== TEST_RESULT ===',"\n";
        var_dump($users_result);
        $this->assertTrue(true);
    }
}

テスト結果

array(3) {
  [1] => array(2) {
    ["name"]  => string(20) "Prof. Wiley Yundt MD"
    ["email"] => string(30) "mosciski.esperanza@example.net"
  }
  [2] => array(2) {
    ["name"]  => string(12) "Easter Fahey"
    ["email"] => string(29) "donnell.bahringer@example.org"
  }
  [3] => array(2) {
    ["name"]  => string(20) "Camille Cummings PhD"
    ["email"] => string(19) "bobby76@example.org"
  }
}

補足1

keyBy() でもいいのでは?

$queryBuilder = self::select(id, name, email);
return $queryBuilder->get()->keyBy('id');

結果

$idが要素側にも入ったままになるため、少し冗長に。

id1 => [id1, name1, email1],
id2 => [id2, name2, email2],
id3 => [id3, name3, email3],

keyBy() は各要素のキーを指定するメソッドであり、要素の内容には触れないためのようです。

補足2

keyBy() + only()では?

$queryBuilder = self::select(id, name, email);
return $queryBuilder->get()->keyBy('id')->only(['name', 'email']);

結果

[]

ええ……どうしてこうなった……

※ こちらは配列変換が再帰的に行われるからのようです。