今日もヤバさをI/O中。

新卒2年目のエンジニアブログです。基本的に何かが足りません。

avgメソッド Collectionメソッドソースコードリーディング100本ノック2本目

メソッド概要

コレクション 5.8 Laravel

avgメソッドは、指定したキーの平均値を返します。

<?php

$average = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo');

// 20

$average = collect([1, 1, 2, 4])->avg();

// 2

average メソッドも同じ処理です。

ソースコードリーディング

avg メソッド

では早速、ソースコードリーディングをやって行きましょう。

<?php

/**
 * Get the average value of a given key.
 *
 * @param  callable|string|null  $callback
 * @return mixed
 */
public function avg($callback = null)
{
    $callback = $this->valueRetriever($callback);

    $items = $this->map(function ($value) use ($callback) {
        return $callback($value);
    })->filter(function ($value) {
        return ! is_null($value);
    });

    if ($count = $items->count()) {
        return $items->sum() / $count;
    }
}

後で解説しますが valueRetriever メソッドは、$callback をコールバックとして読み取れる値に整形するメソッドです。

$callback の戻り値を、 map メソッドを使って Collection へ整形します。

戻り値が null だった時は、 Collection に含めません。 null を平均の対象へと含めないようにする意図のようですね。

最後に Collection 内の値の平均値を返す…といった処理になります。

参考:

github.com

valueRetriever メソッド

<?php

/**
 * Get a value retrieving callback.
 *
 * @param  string  $value
 * @return callable
 */
protected function valueRetriever($value)
{
    if ($this->useAsCallable($value)) {
        return $value;
    }

    return function ($item) use ($value) {
        return data_get($item, $value);
    };
}

$callback を、コールバックとして読み取れる値に整形するメソッドです。

最初にそもそもコールバックとして読み取れる値だったら、そのまま値を返します。

コールバックとして読み取れない値だったら、コールバックとして読み取れる無名関数を返します。例えば以下のようなコードがあったとします。

$callback = $this->valueRetriever($value) 
$callback($item)

この場合、 $callback($item) は以下のようになります。

$callback($item) =
    function ($item) use ($value) {
        return data_get($item, $value);
    }

data_get は、配列やドット区切りのオブジェクトから要素(配列 or データ型の値)を取り出すヘルパメソッドになります(data_get ヘルパメソッドについては、別の記事にて解説します)。

$item が配列だったり Collection だった時のために、このような処理になっているようです。

参考:

github.com

github.com

useAsCallable メソッド

関数として呼び出し可能 かつ 文字列ではないかを返します。

呼び出し可能な関数名ではなく、関数そのものか判別したい時に使うようです。

<?php

/**
 * Determine if the given value is callable, but not a string.
 *
 * @param  mixed  $value
 * @return bool
 */
protected function useAsCallable($value)
{
    return ! is_string($value) && is_callable($value);
}

最後に

この記事を書いているうちに、 Laravel 5.7 -> 5.8 へとバージョンアップしました。

もたもたしているとすぐバージョンが上がってしまい、過去に書いた Collection メソッドをまた再度検証する…なんてことになってしまうので他のメソッドもどんどん書いていこうと思います!

次は chunk メソッドです!