带有 groupby、count 和 sum 的 Laravel Collection

问题描述 投票:0回答:2

我正在努力让一个集合上的 groupby 工作 - 我还没有理解这个概念。

我正在从玩家的表格中提取结果集合,雄辩的集合将包含如下数据:

['player_id'=>1, 'opposition_id'=>10, 'result'=>'won', 'points'=>2],
['player_id'=>1, 'opposition_id'=>11, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>12, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>10, 'result'=>'won', 'points'=>2],
['player_id'=>1, 'opposition_id'=>11, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>10, 'result'=>'lost', 'points'=>0],
['player_id'=>1, 'opposition_id'=>12, 'result'=>'won', 'points'=>2],

我希望能够

groupBy('opposition_id')
,然后给我一个总结果、总胜利、总失败和积分总和的结果,最终得到这样的集合:

['opposition_id'=>10, 'results'=>3, 'won'=>2, 'lost'=>1, 'points'=>4],
['opposition_id'=>11, 'results'=>2, 'won'=>0, 'lost'=>2, 'points'=>0],
['opposition_id'=>10, 'results'=>2, 'won'=>1, 'lost'=>1, 'points'=>2]

我试图避免返回数据库来执行此操作,因为我已经获得了之前活动的结果。

如何使用 Laravel 集合方法来做到这一点,到目前为止我所拥有的是:

$stats = $results->groupBy('opposition_id');

我看过

map()
,但还不明白解决方案的方法

任何人都可以指出我正确的方向吗?

如果需要,很高兴返回数据库,但假设我可以使用已有的集合来执行此操作,而不是创建另一个查询。我在这里找到的解决方案似乎都在查询中提供了解决方案

谢谢你

laravel collections
2个回答
30
投票

看看这里,工作代码和注释中的解释。

// make a collection
$c = collect(
    [
        ['player_id' => 1, 'opposition_id' => 10, 'result' => 'won', 'points' => 2],
        ['player_id' => 1, 'opposition_id' => 11, 'result' => 'lost', 'points' => 0],
        ['player_id' => 1, 'opposition_id' => 12, 'result' => 'lost', 'points' => 0],
        ['player_id' => 1, 'opposition_id' => 10, 'result' => 'won', 'points' => 2],
        ['player_id' => 1, 'opposition_id' => 11, 'result' => 'lost', 'points' => 0],
        ['player_id' => 1, 'opposition_id' => 10, 'result' => 'lost', 'points' => 0],
        ['player_id' => 1, 'opposition_id' => 12, 'result' => 'won', 'points' => 2]
    ]
);
// this only splits the rows into groups without any thing else.
// $groups will be a collection, it's keys are 'opposition_id' and it's values collections of rows with the same opposition_id.
$groups = $c->groupBy('opposition_id'); 

// we will use map to cumulate each group of rows into single row.
// $group is a collection of rows that has the same opposition_id.
$groupwithcount = $groups->map(function ($group) {
    return [
        'opposition_id' => $group->first()['opposition_id'], // opposition_id is constant inside the same group, so just take the first or whatever.
        'points' => $group->sum('points'),
        'won' => $group->where('result', 'won')->count(),
        'lost' => $group->where('result', 'lost')->count(),
    ];
});
// if you don't like to take the first opposition_id you can use mapWithKeys:
$groupwithcount = $groups->mapWithKeys(function ($group, $key) {
    return [
        $key =>
            [
                'opposition_id' => $key, // $key is what we grouped by, it'll be constant by each  group of rows
                'points' => $group->sum('points'),
                'won' => $group->where('result', 'won')->count(),
                'lost' => $group->where('result', 'lost')->count(),
            ]
    ];
});

// here $groupwithcount will give you objects/arrays keyed by opposition_id:
[
  10 =>   ["opposition_id" => 10,"points" => 4,"won" => 2,"lost" => 1]
  11 =>   ["opposition_id" => 11,"points" => 0,"won" => 0,"lost" => 2]
  12 =>   ["opposition_id" => 12,"points" => 2,"won" => 1,"lost" => 1]
]

// if you use $groupwithcount->values() it'll reset the keys to 0 based sequence as usual:
[
  0 =>   ["opposition_id" => 10,"points" => 4,"won" => 2,"lost" => 1]
  1 =>   ["opposition_id" => 11,"points" => 0,"won" => 0,"lost" => 2]
  2 =>   ["opposition_id" => 12,"points" => 2,"won" => 1,"lost" => 1]
]

0
投票

如果处理收集方法,请按

opponent_id
进行分组,然后通过对
pluck()
数据进行
result
处理并解压
countBy()
结果以覆盖默认值,然后简单求和,循环遍历这些组以计算输赢点。要重新索引第一级键,请链接
values()
并转换为数组,请链接
toArray()

请注意,默认情况下,

map()
提供分组行作为第一个参数,分组键作为第二个参数。

如果保证所有小组至少一胜一负,则无需宣布零违约。

代码:(PHPize演示

var_export(
    $coll
    ->groupBy('opposition_id')
    ->map(fn($group, $oppoId) => [
        'opposition_id' => $oppoId,
        'won' => 0,
        'lost' => 0,
        ...$group->pluck('result')->countBy(),
        'points' => $group->sum('points'),
    ])
    ->values()
    ->toArray()
);

或者(PHPize 演示

var_export(
    $coll
    ->groupBy('opposition_id')
    ->map(fn($group, $oppoId) => [
        'opposition_id' => $oppoId,
        'won' => $group->sum(fn($row) => $row['result'] === 'won'),
        'lost' => $group->sum(fn($row) => $row['result'] === 'lost'),
        'points' => $group->sum('points'),
    ])
    ->values()
    ->toArray()
);

结果(来自上面任一脚本):

array (
  0 => 
  array (
    'opposition_id' => 10,
    'won' => 2,
    'lost' => 1,
    'points' => 4,
  ),
  1 => 
  array (
    'opposition_id' => 11,
    'won' => 0,
    'lost' => 2,
    'points' => 0,
  ),
  2 => 
  array (
    'opposition_id' => 12,
    'won' => 1,
    'lost' => 1,
    'points' => 2,
  ),
)

如果此集合数据来自您的数据库,那么构建查询将更加简化。

代码:(PHPize演示

var_export(
    $db::table('results')
    ->select('opponent_id')
    ->selectRaw("SUM(result = 'won') won")
    ->selectRaw("SUM(result = 'lost') lost")
    ->selectRaw("SUM(points) points")
    //->where(['id' => 1])
    ->groupBy('opponent_id')
    ->get()
    ->toArray()
);

这将使用与上一个片段相同的数据填充对象数组。

© www.soinside.com 2019 - 2024. All rights reserved.