Создать новый массив с ключом в качестве значения старого массива с общим количеством?

У меня есть массив со следующими значениями. Я пытаюсь создать новый массив, используя функции массива php массива и стараюсь максимально избежать foreach. Ключ, который мы используем для нового массива, это «статус», и в зависимости от статуса мы создаем новый массив для каждого почтового идентификатора.

<?php
[
{
"mail_id": "29848947",
"last_name": "Doe",
"first_name": "Jon",
"email": "[email protected]",
"status": "opened"},
{
"mail_id": "340980398",
"last_name": "Doe",
"first_name": "Jane",
"email": "[email protected]",
"status": "sent"},
{
"mail_id": "877586",
"last_name": "Dwaye",
"first_name": "Jhon",
"email": "[email protected]",
"status": "clicked"},
{
"mail_id": "225253463",
"last_name": "Doe",
"first_name": "Jon",
"email": "[email protected]",
"status": "opened"},
{
"mail_id": "849849w4",
"last_name": "Doe",
"first_name": "Jane",
"email": "[email protected]",
"status": "sent"}
]
?>

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

<?php
[
[
"first_name": "Jon",
"last_name": "Doe",
"email": "[email protected]",
"opened": 2,
"blocked": 0,
"hard_bounced": 0,
"soft_bounced": 0,
"received": 0,
"clicked": 0
],
[
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"opened": 0,
"blocked": 0,
"hard_bounced": 0,
"soft_bounced": 0,
"sent": 2,
"clicked": 0
],
[
"first_name": "Jhon",
"last_name": "Dwaye",
"email": "[email protected]",
"opened": 0,
"blocked": 0,
"hard_bounced": 0,
"soft_bounced": 0,
"sent": 0,
"clicked": 1
],
]

0

Решение

Использование array_reduce

С помощью array_reduce скорее всего, ваша лучшая ставка, как вы уже догадались. Это своего рода продумывать это как цикл, не используя foreach в явном виде. Вот мое решение, я думаю, это довольно компактно для того, что вы хотите достичь.

$result = array_values(array_reduce($source, function($carry, $event) {
if(!array_key_exists($event['email'], $carry)) {
$carry[$event['email']] = [
"first_name" => $event["first_name"],
"last_name" => $event["last_name"],
"email" => $event["email"],
"opened" => 0,
"blocked" => 0,
"hard_bounced" => 0,
"sent" => 0,
"clicked" => 0
];
}

$carry[$event['email']][$event["status"]]++;

return $carry;
}, []));

Рабочий пример: https://3v4l.org/lhlU0


Использование array_map

Я попробовал другое решение, просто как упражнение. Это не так чисто и компактно, как array_reduce, но иногда это может стоить, по крайней мере, рассмотреть не петлевой подход.

$result = array_map(function($email) use($source) {
$events = array_values(array_filter($source, function($event) use($email) {
return $event['email'] == $email;
}));


return [
"first_name" => $events[0]["first_name"],
"last_name" => $events[0]["last_name"],
"email" => $email,
"opened" => count(array_filter($events, function($event) { return $event["status"] == "opened"; })),
"blocked" => count(array_filter($events, function($event) { return $event["status"] == "blocked"; })),
"hard_bounced" => count(array_filter($events, function($event) { return $event["status"] == "hard_bounced"; })),
"soft_bounced" => count(array_filter($events, function($event) { return $event["status"] == "soft_bounced"; })),
"sent" => count(array_filter($events, function($event) { return $event["status"] == "sent"; })),
"clicked" => count(array_filter($events, function($event) { return $event["status"] == "clicked"; })),
];
}, array_unique(array_column($source, "email")));

Рабочий пример: https://3v4l.org/KSGeX

Хотя я бы сказал, что те count(array_filter(... вызовы должны быть абстрагированы в отдельную функцию:

function countEvents($events, $status) {
return count(array_filter($events, function($event) use($status) {
return $event["status"] == $status;
}));
}

Так что теперь в приведенном выше массиве вы можете просто countEvents($events, "opened") например. Сделаю это намного чище.

2

Другие решения

Я не очень понимаю, почему не хочу использовать foreach, основная часть кода такая же, что-то подобное должно делать работу.

Я предполагаю, что данные находятся в многомерном массиве и что имя массива $ old_records;

-> С Для каждого

$new_records = [];

foreach ($old_records as $old_record) {

if(!array_key_exists($old_record["email"], $new_records)) {
$new_records[$old_record["email"]] = [
"opened"       => 0,
"blocked"      => 0,
"hard_bounced" => 0,
"soft_bounced" => 0,
"received"     => 0,
"clicked"      => 0,
"sent"         => 0,
];
}

$new_record = &$new_records[$old_record["email"]];

$new_record["first_name"] = $old_record["first_name"];
$new_record["last_name"] = $old_record["last_name"];
$new_record["email"] = $old_record["email"];

if(!array_key_exists($old_record["status"], $new_record)) {
$new_record[$old_record["status"]] = 0;
}

$new_record[$old_record["status"]]++;
}

-> С array_reduce

function format($carry, $item) {

if (empty($carry)) {
$carry = [];
}

if ( ! array_key_exists($item[ "email" ], $carry)) {
$carry[ $item[ "email" ] ] = [
"opened"       => 0,
"blocked"      => 0,
"hard_bounced" => 0,
"soft_bounced" => 0,
"received"     => 0,
"clicked"      => 0,
"sent"         => 0,
];
}

$new_record = &$carry[ $item[ "email" ] ];

$new_record[ "first_name" ] = $item[ "first_name" ];
$new_record[ "last_name" ]  = $item[ "last_name" ];
$new_record[ "email" ]      = $item[ "email" ];

if ( ! array_key_exists($item[ "status" ], $new_record)) {
$new_record[ $item[ "status" ] ] = 0;
}

$new_record[ $item[ "status" ] ] ++;

return $carry;
}

array_reduce($old_records, "format");

@ЗаметкаЯ использовал электронную почту в качестве ключа для объединения данных и установки некоторых значений по умолчанию для статусов, потому что в примере вы возвращаете 0 с определенным отсутствующим статусом.

1

Мои полученные ключевые заказы немного отличаются:

<?php
$json =<<<JSON
[
{
"mail_id": "29848947",
"last_name": "Doe",
"first_name": "Jon",
"email": "[email protected]",
"status": "opened"},
{
"mail_id": "340980398",
"last_name": "Doe",
"first_name": "Jane",
"email": "[email protected]",
"status": "sent"},
{
"mail_id": "877586",
"last_name": "Dwaye",
"first_name": "Jhon",
"email": "[email protected]",
"status": "clicked"},
{
"mail_id": "225253463",
"last_name": "Doe",
"first_name": "Jon",
"email": "[email protected]",
"status": "opened"},
{
"mail_id": "849849w4",
"last_name": "Doe",
"first_name": "Jane",
"email": "[email protected]",
"status": "sent"}
]
JSON;

Метод:

$data = json_decode($json, true);

$status_keys = [
'opened',
'blocked',
'hardbouced',
'softbounced',
'sent',
'clicked'
];

$skel = array_fill_keys($status_keys, 0);

foreach($data as $item) {
$email  = $item['email'];
$status = $item['status'];
unset($item['status'], $item['mail_id']);

if(!isset($result[$email]))
$result[$email] = array_merge($item, $skel);

$result[$email][$status]++;
}
asort($result);
echo json_encode(array_values($result), JSON_PRETTY_PRINT);

Выход:

[
{
"last_name": "Doe",
"first_name": "Jane",
"email": "[email protected]",
"opened": 0,
"blocked": 0,
"hardbouced": 0,
"softbounced": 0,
"sent": 2,
"clicked": 0
},
{
"last_name": "Doe",
"first_name": "Jon",
"email": "[email protected]",
"opened": 2,
"blocked": 0,
"hardbouced": 0,
"softbounced": 0,
"sent": 0,
"clicked": 0
},
{
"last_name": "Dwaye",
"first_name": "Jhon",
"email": "[email protected]",
"opened": 0,
"blocked": 0,
"hardbouced": 0,
"softbounced": 0,
"sent": 0,
"clicked": 1
}
]
0
По вопросам рекламы [email protected]