按一列对 2d 数组数据进行分组,使用默认值填充子数组,使用映射数组对另一列的值求和

Group 2d array data by one column, populate subarrays with defaults, sum values from another column using a mapping array

提问人:devie 提问时间:11/17/2023 最后编辑:mickmackusadevie 更新时间:11/17/2023 访问量:62

问:

我正在尝试对 2d 数组中的数据进行分组,同时利用查找数组来确保每个组中都存在默认值。

我的查找/映射数组将语言 ID 与语言名称相关联。

$langs = [
    5 => "english",
    10 => "french",
    12 => "german"
    ...
];

另一个数组保存应按日期值分组的数据。

$posts = [
    [
        "date" => "13-07-2022",
        "lang_id" => 5,
        "amount" => 90,
    ],
    [
        "date" => "13-07-2022",
        "lang_id" => 10,
        "amount" => 34,
    ],
    [
        "date" => "14-07-2022",
        "lang_id" => 5,
        "amount" => 7,
    ],
    ...
];

每天以该语言发布帖子的地方。当没有任何帖子时,数组中不会有任何条目。$posts

我想重构要按日期分组的数据,每个子数组都应包含日期和累积的关联语言量:

$result = [
    [
        "date" => "13-07-2022",
        "english" => 90,
        "french" => 34,
        "german" => 0
    ],
    [
        "date" => "14-07-2022",
        "english" => 6,
        "french" => 0,
        "german" => 0
    ],
    ...
];

也就是说,当“主”语言列表中没有帖子时,我会将该语言设置为 0。

我试图迭代数组并在其中迭代数组,以便将每个新处理的项目填充为零,但我未能实现它。 我尝试的是:$postslangs

$d = $post[0]['date'];
$result = [];

foreach ($posts as $post) {
    if ($d != $post['date']) {
        $d = $post['date'];
        $result[] = $proc;
        $proc = [];
        $proc['date'] = $d;
    }

    foreach ($langs as $id => $name) {
        if ($id == $post['lang_id']) {
            $proc[$name] = $post['amount'];
        } elseif (!isset($proc[$name])) {
            $proc[$name] = 0;
        }
    }
}

$result[] = $proc;

我的观点是,是否有更好的方法可以在没有嵌套循环或更有效的情况下实现这一点

PHP 数组 总和 映射 分组

评论

0赞 TSCAmerica.com 11/17/2023
为此,可以先创建一个所有语言都设置为 0 的模板数组,然后遍历$posts以填充值。
0赞 Barmar 11/17/2023
如果结果是一个键是日期的关联数组,则这更容易。

答:

2赞 Nigel Ren 11/17/2023 #1

如果遍历 posts 数组并针对每个项目,请首先检查此日期是否已经有结果。如果没有 - 它会将 langs in 中的值与值 0 相加,然后添加日期(您可以先使用 )。array_fill_keys()array_merge()

然后它总是添加各种键......

$results = [];

foreach ($posts as $post) {
    if (array_key_exists($post['date'], $results) == false) {
        $results[$post['date']] = array_fill_keys($langs, 0);
        $results[$post['date']]['date'] = $post['date'];
    }
    $results[$post['date']][$langs[$post['lang_id']]] = $post['amount'];
}

var_dump(array_values($results));

这给了...

array(2) {
  [0] =>
  array(4) {
    'english' =>
    int(90)
    'french' =>
    int(34)
    'german' =>
    int(0)
    'date' =>
    string(10) "13-07-2022"
  }
  [1] =>
  array(4) {
    'english' =>
    int(7)
    'french' =>
    int(0)
    'german' =>
    int(0)
    'date' =>
    string(10) "14-07-2022"
  }
}

为了把日期放在第一位......

$results[$post['date']] = array_merge(['date' => $post['date']], array_fill_keys($langs, 0));
0赞 lukas.j 11/17/2023 #2
$langs = [
  5  => 'english',
  10 => 'french',
  12 => 'german',
];

$posts = [
  [ 'date' => '13-07-2022', 'lang_id' => 5, 'amount' => 90, ],
  [ 'date' => '13-07-2022', 'lang_id' => 10, 'amount' => 34, ],
  [ 'date' => '14-07-2022', 'lang_id' => 5, 'amount' => 7, ],
];

$langs_template = array_combine($langs, array_fill(0, count($langs), 0));

$result =
  array_values(
    array_reduce(
      $posts,
      function ($carry, $item) use ($langs, $langs_template) {
        $date = $item['date'];
        if (!array_key_exists($date, $carry)) {
          $carry[$date] = [ 'date' => $date, ...$langs_template ];
        }
        $carry[$date][$langs[$item['lang_id']]] = $item['amount'];
        return $carry;
      },
      []
    )
  );

print_r($result);

输出:

Array
(
    [0] => Array
        (
            [date] => 13-07-2022
            [english] => 90
            [french] => 34
            [german] => 0
        )

    [1] => Array
        (
            [date] => 14-07-2022
            [english] => 7
            [french] => 0
            [german] => 0
        )

)

评论

0赞 mickmackusa 11/17/2023
请不要脚本转储。每个答案都应该包括一个教育解释。你不是新来的。
0赞 mickmackusa 11/17/2023 #3
  1. 在循环之前声明要用于每个日期组的默认值数组
  2. 遍历 posts 数组
  3. 用于为每次迭代生成方便的临时变量extract()
  4. 如果遇到的日期是新的,请设置默认值
  5. 利用查找数组将当前金额添加到正确的语言列

代码:(演示)

$defaults = array_fill_keys($langs, 0);
$result = [];
foreach ($posts as $row) {
    extract($row);
    $result[$date] ??= ['date' => $date] + $defaults;
    $result[$date][$langs[$lang_id]] += $amount;
}
var_export(array_values($result));

若要避免在循环后重新索引结果,请将引用推送到结果中。演示

$defaults = array_fill_keys($langs, 0);
$result = [];
foreach ($posts as $row) {
    extract($row);
    if (!isset($ref[$date])) {
        $ref[$date] = ['date' => $date] + $defaults;
        $result[] = &$ref[$date];
    }
    $ref[$date][$langs[$lang_id]] += $amount;
}
var_export($result);