skip to Main Content

I have 2 arrays in the shape:

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

Where the keys are the ids of the languages.

And:

$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,
    ],
...
];

Where the $posts have the post in that language per day. When there weren’t any posts, there won’t be any entry in the array.

What I want is to process the posts array to have something like:

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

This is, when there no posts from the "master" list of languages, I will set that language to 0.

I tried to iterate the $posts and within it the langs array in order to fill with zeros each new processed item, but I’m failing to fulfil it.
What I tried is:

$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;

My point is if there would be a better way to achieve this with no nested loop or more efficient way

3

Answers


  1. If you loop through the posts array and for each item, first check if there is already a result for this date. If there isn’t – it adds the values from langs in using array_fill_keys() with a value of 0, and then adds the date in (you could make the date first by using array_merge()).

    Then it always adds in the various keys…

    $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));
    

    which gives…

    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"
      }
    }
    

    For putting the date first…

    $results[$post['date']] = array_merge(['date' => $post['date']], array_fill_keys($langs, 0));
    
    Login or Signup to reply.
  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);
    

    Output:

    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
            )
    
    )
    
    Login or Signup to reply.
    1. Declare a default values array to be used for each date group before looping
    2. Loop over the posts array
    3. Use extract() to generate convenient temporary variables for each iteration
    4. If an encountered date is new, set the default values
    5. Add the current amount to the correct language column leveraging the lookup array

    Code: (Demo)

    $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));
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search