Jon
Jon

Reputation: 109

Merge multiple CodeIgniter result sets into one array

Data queried from the database.

year 2021 = [
    {
        "month": 1,
        "total": "1,482"
    },
    {
        "month": 2,
        "total": "5,422"
    },
]

and

year 2020 = [
    {
        "month": 1,
        "total": "2,482"
    },
    {
        "month": 2,
        "total": "6,422"
    },
    {
        "month": 3,
        "total": "7,422"
    },
    .........
    {
        "month": 12,
        "total": "20,422"
    },
]

Here I create a method called `GetData(), I create a loop from 1-12 based on the months in a year. Next I want to combine data in an array, but how do I combine the data?

Below is my controller model which calls the same model method over and over with different parameters:

public function GetData(){
    $thn_now = 2021; 
    $thn_before = 2020; 
    $bulan = array();
    for ($bul = 1; $bul <= 12; $bul++) {
        $sql = $this->My_model->model_month($bul,$thn_now)->result();  // Here I make the current year parameter
        // $sql2 = $this->My_model->model_month($bul,$thn_before)->result();  //here are the parameters of the previous year
        foreach($sql as $row) {
            $bulan[] = array(
                'bulan' => $bul,
                'total_now' => $row->hasil,
                // how to display the previous year's total in an array ?
                // 'total_before' => $row->hasil,
            );
        }
    }
    $this->set_response($bulan,200);  
}

I want an output like this:

[
    {
        "month": 1,
        "total_now": "1,482"
        "total_before": "2,482"
    },
    {
        "month": 2,
        "total_now": "5,522"
        "total_before": "6,422"
    },
    {
        "month": 3,
        "total_now": null 
        "total_before": "7,422"
    },
    .........
    {
        "month": 12,
        "total_now": null,
        "total_before": "20,422"
    },
]

In 2021, the total is only until the month of 02, while in 2020 the total is until month 12.

If 2021 is only up to month 02 then the next array is total null.

Upvotes: 0

Views: 580

Answers (2)

mickmackusa
mickmackusa

Reputation: 47864

  1. I don't recommend that you make 24 trips to the database to collect individual totals; I'll urge you to create a new model method which delivers all of the monthly totals in a single trip.
  2. I don't recommend allowing your controller to dictate the type of result set that is created from the active record in the model. The model should be consistently returning the same data in the same structure throughout your application.
  3. It seems to me that your inner loop is only collecting one row anyhow, so instead of result() which creates an array of objects, just call row() which creates null or an object then immediately access the hasil property. Using the null coalescing operator, you can fall back to null when hasil is not a property of non-object null.
  4. Professional programmers around the world (including ones that don't use English as their first language will recommend writing variables using English words. This allows a more consistent language for other developers to review and maintain your code. After all, you are using English in the rest of your php syntax, just keep it all in English.
  5. For future flexibility, allow the "current year" to be passed in as a controller method argument. This will allow you to seamlessly view historic data (should the need ever arise). If there is no passed in argument, then default to the current year.
  6. Hard coding the number of each month is acceptable because it will never change. However, as a general rule, try to avoid using hardcoded numbers/values in your script so that it is more flexible, easier to maintain, and more intuitive to read.

Suggested code (tolerating that you are passing up to 24 active record objects back to the controller):

public function GetMonthlyTotalsSincePreviousYear($year = null): void
{
    $thisYear = $year ?? date('Y');
    $lastYear = $thisYear - 1;
    $result = [];
    for ($month = 1; $month <= 12; ++$month) {
        $result[] = [
            'month' => $month,
            'total_now' => $this->My_model->model_month($month, $thisYear)->row()->hasil ?? null,
            'total_before' => $this->My_model->model_month($month, $lastYear)->row()->hasil ?? null,
        ];
    }
     $this->set_response($result, 200);  
}

In my own application, I would have a model method that collects all of the data and a controller like this:

public function getMonthlyTotalsSincePreviousYear(int $year = null): void
{
    $this->set_response(
        $this->My_model->getMonthlyTotalsSincePreviousYear($year ?? date('Y')),
        200
    );
}

Upvotes: 2

Sibi Kandathil
Sibi Kandathil

Reputation: 126

This, I believe, will assist you in developing the solution. I haven't included the entire array of $year_2020, but I hope you get the idea -

$year_2021 = [
   [
    "month" => 1,
    "total_now" => "1,482"
   ],
   [
    "month" => 2,
    "total_now" => "5,422"
   ]
  ];

  $year_2020 = [
   [
    "month" => 1,
    "total_before" => "2,482"
   ],
   [
    "month" => 2,
    "total_before" => "6,422"
   ],
   [
    "month" => 3,
    "total_before" => "7,422"
   ]
  ];

  $output = [];

  foreach ($year_2021 as $obj) {
     $key = $obj['month'];
     $output[$key] = $obj;
  }

 foreach ($year_2020 as $obj) {
    $key = $obj['month'];
    if (isset($output[$key])) {
      $output[$key]['total_before'] = $obj['total_before'];
    } else {
      $obj['total_now'] = null;
      $output[$key] = $obj;
    }
   }

    $output = array_values($output);
    error_log(json_encode($output));

Output

[{"month":1,"total_now":"1,482","total_before":"2,482"},{"month":2,"total_now":"5,422","total_before":"6,422"},{"month":3,"total_before":"7,422","total_now":null}]

Upvotes: 1

Related Questions