Merge 2 arrays with null values - php

I want to merge 2 tables I show you an example:
foreach ($dashboardData as $item) {
$results[$item['month']] = [
DashboardConsts::COLUMN_MONTHLY => $item['month'] ?? null,
DashboardConsts::ROW_NBR => $item['nbr'] ?? null,
DashboardConsts::ROW_SELL_PRICE => $item['sum'] ?? null,
'number' => $item['countNewBc'] ?? null,
];
}
array:2 [▼
"2020-12" => array:4 [▼
"Mois" => "2020-12"
"Nbr vendus" => null
"CA TTC" => null
"number" => "1"
]
"2021-01" => array:4 [▼
"Mois" => "2021-01"
"Nbr vendus" => null
"CA TTC" => null
"number" => "2"
]
]
and
"2020-12" => array:4 [▼
"Mois" => "2020-12"
"Nbr vendus" => "1"
"CA TTC" => "790"
"number" => null
]
"2021-01" => array:4 [▼
"Mois" => "2021-01"
"Nbr vendus" => "3"
"CA TTC" => "1680"
"number" => null
]
Basically here, I make a sort of group my values ​​by period, I try to merge my 2 arrays to ensure that the fields which are 'null'.
I want to replace null values ​​with those that are filled in one of the array.
I had to try to do something like that :
$tableau1 = array_filter($tableau1);
$tableau2 = array_filter($tableau2);
$result = array_merge($tableau1, $tableau2);
but it does not work.
EDIT:
I wish my final result looks like this
array:2 [▼
"2020-12" => array:4 [▼
"Mois" => "2020-12"
"Nbr vendus" => "1"
"CA TTC" => "790"
"number" => "1"
]
"2021-01" => array:4 [▼
"Mois" => "2021-01"
"Nbr vendus" => "3"
"CA TTC" => "1680"
"number" => "2"
]
]

There are many ways of achieving this, all the solutions, needs to go through the arrays, one way or another.
You easily do a foreach () loop marathon and and loads of if () statements to make sure the conversion is correctly made.
Anyways, this is my take on the question.
Remove null values and replace them with array_replace_recursive():
<?php
function array_remove_null($input) {
foreach ($input as &$value) {
if (is_array($value)) {
$value = array_remove_null($value);
}
}
return array_filter($input, function($item){
return $item !== null && $item !== '';
});
}
/**
* Using https://www.php.net/manual/en/function.array-replace-recursive.php
*/
$array1 = [
"2020-12" => [
"Mois" => "2020-12",
"Nbr vendus" => null,
"CA TTC" => null,
"number" => "1",
],
"2021-01" => [
"Mois" => "2021-01",
"Nbr vendus" => null,
"CA TTC" => null,
"number" => "2",
],
];
$array2 = [
"2020-12" => [
"Mois" => "2020-12",
"Nbr vendus" => "1",
"CA TTC" => "790",
"number" => null,
],
"2021-01" => [
"Mois" => "2021-01",
"Nbr vendus" => "3",
"CA TTC" => "1680",
"number" => null,
],
];
$array = array_replace_recursive(array_remove_null($array1), array_remove_null($array2));
var_dump($array);
In action: https://3v4l.org/WJnYu

I think I understand. You need a nested loop to access both sets of data. The following might not be the actual answer, but it's close.
$results = array();
foreach ($inputArrayOne as $onekey => $arrayOne) {
$temp_results = array();
foreach ($inputArrayTwo as $twokey => $arrayTwo) {
if ($onekey != $twokey) {
continue;
}
if (!is_null($arrayOne['month'])) {
$temp_results['Mois'] = $arrayOne['month'];
} else if (!is_null($arrayTwo['month'])) {
$temp_results['Mois'] = $arrayTwo['month'];
} else {
$temp_results['Mois'] = null; // to account for bad data
}
if (!is_null($arrayOne['nbr'])) {
$temp_results['Nbr vendus'] = $arrayOne['nbr'];
} else if (!is_null($arrayTwo['nbr'])) {
$temp_results['Nbr vendus'] = $arrayTwo['nbr'];
} else {
$temp_results['Nbr vendus'] = null; // to account for bad data
}
if (!is_null($arrayOne['sum'])) {
$temp_results['CA TTC'] = $arrayOne['sum'];
} else if (!is_null($arrayTwo['sum'])) {
$temp_results['CA TTC'] = $arrayTwo['sum'];
} else {
$temp_results['CA TTC'] = null; // to account for bad data
}
if (!is_null($arrayOne['countNewBc'])) {
$temp_results['number'] = $arrayOne['countNewBc'];
} else if (!is_null($arrayTwo['countNewBc'])) {
$temp_results['number'] = $arrayTwo['countNewBc'];
} else {
$temp_results['number'] = null; // to account for bad data
}
}
$results[$arrayOne['month']] = $temp_results;
}

I know you got your answer, but if someone stumbles upon this question and don't need recursive (only one level down in array) check then you could do this:
(My logic is simply to replace null with zeros and add together equivalent keys from the arrays. In my code I also take for granted that there are same the same keys in inner array for each array)
$new_arr = array_slice($array1,0);
foreach($array1 as $headkey=>$item) {
foreach($item as $innerkey=>$it) {
$null_change = false;
if ($array1[$headkey][$innerkey] === null) {
$array1[$headkey][$innerkey] == 0;
$null_change = true;
}
if ($array2[$headkey][$innerkey] === null) {
$array2[$headkey][$innerkey] == 0;
$null_change = true;
}
if ($null_change === true) {
$new_arr[$headkey][$innerkey] = $array1[$headkey][$innerkey] +
$array2[$headkey][$innerkey];
}
}
}
var_dump($new_arr);
If you need a more generic result based on unknown number of arrays you could do this (but still only one level in each array):
function replaceNullOccurences(...$arrays) {
if (!is_array($arrays[0])) return;
$new_arr = array_slice($arrays[0],0);
foreach($arrays[0] as $headkey=>$item) {
foreach($item as $innerkey=>$it) {
$null_change = false;
foreach($arrays as $arr) {
if ($arr[$headkey][$innerkey] === null) {
$arr[$headkey][$innerkey] == 0; //Change null to zero for addition below
$null_change = true;
}
}
//If null in the key in any array, sum together the arrays
if ($null_change === true) {
foreach($arrays as $arr) {
$new_arr[$headkey][$innerkey] += $arr[$headkey][$innerkey];
}
}
}
}
return $new_arr;
}
$result = replaceNullOccurences($array1, $array2);

Related

PHP - Flat Associative Array into Deeply nested Array by parent property

I have a problem that has been stressing me out for weeks now and i cannot find a clean solution to it that does not involve recursion.
This is the problem:
Take a flat array of nested associative arrays and group this into one deeply nested object. The top level of this object will have its parent property as null.
This is my solution but i admit it is far from perfect. I am fairly certain this can be done in a single loop without any recursion, but for the life of me i cannot work it out!
//Example single fork
$data = array(
//Top of Tree
0 => array(
"name" => "A",
"parent" => null,
"id" => 1,
),
//B Branch
1 => array(
"name" => "B",
"parent" => "1",
"id" => 2,
),
2 => array(
"name" => "B1",
"parent" => "2",
"id" => 3,
),
3 => array(
"name" => "B2",
"parent" => "3",
"id" => 4,
),
4 => array(
"name" => "B3",
"parent" => "4",
"id" => 5,
),
//C Branch
5 => array(
"name" => "C",
"parent" => "1",
"id" => 6,
),
6 => array(
"name" => "C1",
"parent" => "6",
"id" => 7,
),
7 => array(
"name" => "C2",
"parent" => "7",
"id" => 8,
),
8 => array(
"name" => "C3",
"parent" => "8",
"id" => 9,
),
);
Actual anonymised example
array:7214 [▼
0 => array:3 [▼
"name" => ""
"parent" => null
"id" =>
]
1 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
2 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
3 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
4 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
5 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
6 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
7 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
8 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
9 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
10 => array:3 [▼
"name" => ""
"parent" =>
"id" =>
]
Another example deeper nesting
{
"name":"top",
"id":xxx,
"children":{
"second":{
"name":"second",
"id":xxx,
"children":{
"Third":{
"name":"third",
"id":xxx,
"children":{
"fourth":{
"name":"fourth",
"id":xxx
}
}
}
}
}
}
}
$originalLength = count($data);
$obj = [];
while ($originalLength > 0) {
foreach ($data as $item) {
$name = $item['name'];
$parent = $item['parent'];
$a = isset($obj[$name]) ? $obj[$name] : array('name' => $name, 'id'=>$item['id']);
if (($parent)) {
$path = get_nested_path($parent, $obj, array(['']));
try {
insertItem($obj, $path, $a);
} catch (Exception $e) {
continue;
//echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
$obj[$name] = isset($obj[$name]) ? $obj[$name] : $a;
$originalLength--;
}
}
echo json_encode($obj['A']);
function get_nested_path($parent, $array, $id_path)
{
if (is_array($array) && count($array) > 0) {
foreach ($array as $key => $value) {
$temp_path = $id_path;
array_push($temp_path, $key);
if ($key == "id" && $value == $parent) {
array_shift($temp_path);
array_pop($temp_path);
return $temp_path;
}
if (is_array($value) && count($value) > 0) {
$res_path = get_nested_path(
$parent, $value, $temp_path);
if ($res_path != null) {
return $res_path;
}
}
}
}
return null;
}
function insertItem(&$array, $path, $toInsert)
{
$target = &$array;
foreach ($path as $key) {
if (array_key_exists($key, $target))
$target = &$target[$key];
else throw new Exception('Undefined path: ["' . implode('","', $path) . '"]');
}
$target['children'] = isset($target['children']) ? $target['children'] : [];
$target['children'][$toInsert['name']] = $toInsert;
return $target;
}
Here's my take on what I believe is the desired output:
function buildTree(array $items): ?array {
// Get a mapping of each item by ID, and pre-prepare the "children" property.
$idMap = [];
foreach ($items as $item) {
$idMap[$item['id']] = $item;
$idMap[$item['id']]['children'] = [];
}
// Store a reference to the treetop if we come across it.
$treeTop = null;
// Map items to their parents' children array.
foreach ($idMap as $id => $item) {
if ($item['parent'] && isset($idMap[intval($item['parent'])])) {
$parent = &$idMap[intval($item['parent'])];
$parent['children'][] = &$idMap[$id];
} else if ($item['parent'] === null) {
$treeTop = &$idMap[$id];
}
}
return $treeTop;
}
This does two array cycles, one to map up the data by ID, then one to assign children to parents. Some key elements to note:
The build of $idMap in the first loop also effectively copies the items here so we won't be affecting the original input array (Unless it already contained references).
Within the second loop, there's usage of & to use references to other items, otherwise by default PHP would effectively create a copy upon assignment since these are arrays (And PHP copies arrays on assignment unlike Objects in PHP or arrays in some other languages such as JavaScript). This allows us to effectively share the same array "item" across the structure.
This does not protect against bad input. It's possible that invalid mapping or circular references within the input data could cause problems, although our function should always just be performing two loops, so should at least not get caught in an infinite/exhaustive loop.

Getting zero when extracting data from a PHP array in Laravel app

Am working on some set of PHP array. Am trying to loop through each of them and check
the array whose name is equal to Josw Acade. Am using a for loop but I get zero
after extracting the data. I want to store the data in an array.
Array
array:6 [
0 => array:4 [
"id" => 1
"name" => "Josw Acade"
"value" => "Unlimited"
"plan_type" => "Superior"
]
1 => array:4 [
"id" => 2
"name" => "Verbal"
"value" => "true"
"plan_type" => "Superior"
]
2 => array:4 [
"id" => 12
"name" => "Josw Acade"
"value" => "$1,500,00"
"plan_type" => "Classic"
]
3 => array:4 [
"id" => 13
"name" => "Leon"
"value" => "true"
"plan_type" => "Classic"
]
4 => array:4 [
"id" => 14
"name" => "One Time"
"value" => "true"
"plan_type" => "Classic"
]
5 => array:4 [
"id" => 15
"name" => "Deat"
"value" => "$25,000"
"plan_type" => "Classic"
]
6 => array:4 [
"id" => 23
"name" => "Josw Acade"
"value" => "$100,000"
"plan_type" => "Essential"
]
]
Logic
$Inst = [];
for($med = 0; $med < count($array); $med++){
if($med['name'] == "Josw Acade"){
$Inst = $med['value'];
}
}
dd($Inst);
Your variables is not corretly set in the for loop, you are setting $med = 0 and acessing $med as an array.
Use filter, that runs a condition on each element and returns the items that satisfy that condition.
array_filter($array, function ($item) {
return $item['name'] === 'Josw Acade';
});
In general you don't have to make old school arrays anymore, foreach does the same.
$results = [];
foreach($array as $item)
{
if ($item['name'] === 'Josw Acade') {
$results[] = $item['value'];
}
}
You can use array_filter with callback
$filtered = array_filter($array, function($v){ return $v['name'] == 'Josw Acade'})
print_r($filtered);
You are looping through array; so on each iteration to get values; you need to pass index value and you are missing that. You are using $med as index.
Here is code.
$Inst = [];
for($med = 0; $med < count($array); $med++){
if($array[$med]['name'] == "Josw Acade"){
$Inst[] = $array[$med]['value'];
}
}
there is many way to do this but according to me the best way to use array_filer()
array_filter($array, function ($item) {
return $item['name'] === 'Josw Acade';
});

php/laravel: Creating chartdata, return 0 if no data is being submitted to database

I am trying to create a chart from data returned by a databse. The data is stored in a database and there can be multiple charts in this table. Identified by a unique value. (rigname)
array:102 [▼
0 => array:6 [▼
"id" => 2002
"user_id" => "1"
"rigname" => "home"
"data" => "{"hashrate":12.364,"pool":"eu1.ethermine.org:4444","shares":{"valid":"2","invalid":"0"},"gpus":[{"temperature":"56","fanSpeed":"56","hashrates":12.364}]}"
"created_at" => "2018-02-23 10:24:50"
"updated_at" => "2018-02-23 10:24:50"
],
1 => array:6 [▼
"id" => 2019
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.612,"pool":"eu1.ethermine.org:4444","shares":{"valid":"736","invalid":"2"},"gpus":[{"temperature":"65","fanSpeed":"67","hashrates":19.804},{"temp ▶"
"created_at" => "2018-02-23 10:25:56"
"updated_at" => "2018-02-23 10:25:56"
]
As you can see there are two possible charts according to the rigname. I want to display the hashrate value of the two rigs on a chart. This works, but, when one rig stops there is no data being submitted to the database from that rig.
The chart displays no 0 value for that rig, the last known data is being used. (duh)
I solved this by modifying my query like this:
$res[$value] = json_decode(MinerStatistics::where('rigname', $value)
->where('created_at','>=', \Carbon\Carbon::now()->subMinutes(20))
->orderBy('created_at', 'desc')
->get()
->toJson(), true
);
Now when the rig is stopped and I query the database, I only get the data from the past x minutes. This is done in a foreach that builds a new multidimensional array with the data sorted per rigname.
This rises a new problem, when I restart the rig and it starts reporting to the database. The values on the chart continue not at the current timestamp, they continue where the rig has stopped. Normally I would compare the timestamp of the last reported data and the previous reported data with the data of the rig that is still running and return 0 every time a running rig reports, but they report different timestamps ( all within 8 sec of each other)
How can I modify the returned mysql data that I end up with something like this:
array:2 [▼
"home" => array:150 [
0 => array:6 [▼
"id" => 2300
"user_id" => "1"
"rigname" => "home"
"data" => "{"hashrate":12.359,"pool":"eu1.ethermine.org:4444","shares":{"valid":"5","invalid":"0"},"gpus":[{"temperature":"57","fanSpeed":"57","hashrates":12.359}]}"
"created_at" => "2018-02-23 10:44:42"
"updated_at" => "2018-02-23 10:44:42"
]
1 => array:6 [▼
"id" => 2298
"user_id" => "1"
"rigname" => "home"
"data" => "{"hashrate":12.367,"pool":"eu1.ethermine.org:4444","shares":{"valid":"5","invalid":"0"},"gpus":[{"temperature":"57","fanSpeed":"57","hashrates":12.367}]}"
"created_at" => "2018-02-23 10:44:34"
"updated_at" => "2018-02-23 10:44:34"
]
2 => array:6 [▼
"id" => 2296
"user_id" => "1"
"rigname" => "home"
"data" => "{"hashrate":12.367,"pool":"eu1.ethermine.org:4444","shares":{"valid":"5","invalid":"0"},"gpus":[{"temperature":"57","fanSpeed":"57","hashrates":12.367}]}"
"created_at" => "2018-02-23 10:44:26"
"updated_at" => "2018-02-23 10:44:26"
]
...
Server stops, no data, should continue like this
3 => [false] ,
4 => [false] ,
5 => [false]
...
rig reports again to the database
36 => array:6 [▼
"id" => 2296
"user_id" => "1"
"rigname" => "home"
"data" => "{"hashrate":12.367,"pool":"eu1.ethermine.org:4444","shares":{"valid":"5","invalid":"0"},"gpus":[{"temperature":"57","fanSpeed":"57","hashrates":12.367}]}"
"created_at" => "2018-02-23 10:44:26"
"updated_at" => "2018-02-23 10:44:26"
],
],
"server" => array:150 [▼ // This keeps running, or visa versa
0 => array:6 [▼
"id" => 2301
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.62,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"67","hashrates":19.778},{"tempe ▶"
"created_at" => "2018-02-23 10:44:44"
"updated_at" => "2018-02-23 10:44:44"
]
1 => array:6 [▼
"id" => 2299
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.68,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"65","hashrates":19.815},{"tempe ▶"
"created_at" => "2018-02-23 10:44:36"
"updated_at" => "2018-02-23 10:44:36"
]
2 => array:6 [▼
"id" => 2297
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.644,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"65","hashrates":19.793},{"temp ▶"
"created_at" => "2018-02-23 10:44:28"
"updated_at" => "2018-02-23 10:44:28"
]
3 => array:6 [▼
"id" => 2295
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.105,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"65","hashrates":19.793},{"temp ▶"
"created_at" => "2018-02-23 10:44:20"
"updated_at" => "2018-02-23 10:44:20"
]
4 => array:6 [▼
"id" => 2293
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.557,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"65","hashrates":19.78},{"tempe ▶"
"created_at" => "2018-02-23 10:44:12"
"updated_at" => "2018-02-23 10:44:12"
]
5 => array:6 [▼
"id" => 2291
"user_id" => "1"
"rigname" => "server"
"data" => "{"hashrate":76.562,"pool":"eu1.ethermine.org:4444","shares":{"valid":"749","invalid":"2"},"gpus":[{"temperature":"64","fanSpeed":"65","hashrates":19.794},{"temp ▶"
"created_at" => "2018-02-23 10:44:04"
"updated_at" => "2018-02-23 10:44:04"
]
]
]
The current code I have written:
public function index()
{
// Get the statistics from the database reported by the miner
// Group them by the rig name
$stats = MinerStatistics::where('user_id', Auth::user()->id)
->where('created_at','>=', Carbon::now()->subMinutes(20))
->orderBy('created_at', 'desc')
->take(300)
->get()
->groupBy('rigname');
// Get the rig names
$rigs = (object) [];
foreach($stats as $key => $value) {
$rigs->$key = $key;
}
// For each rig, get the last known stats between now and 10 seconds ago
$table = (object) [];
foreach($rigs as $key => $value) {
$tableData = $stats[$value]
->where('created_at','>=', Carbon::now()->subSeconds(10));
if (isset($tableData[0]) && $tableData !== []) {
$table->$key = (object) $tableData[0];
$table->$key->data = json_decode($tableData[0]['data'], true);
}
}
return view('rigs', [
'rigs' => $table,
'chart' => $this->chart($stats)
]);
}
public function chart($data)
{
$p = [];
foreach($data as $key => $value) {
$x = [];
$time = [];
foreach($value as $k => $v) {
$time[] = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $v['created_at'])->format('H:i:s');
$x[] = json_decode($v['data'], true);
}
$hashrate = [];
foreach($x as $k => $v){
$hashrate[] = $v['hashrate'];
}
$p[$key] = [ 'time' => $time, 'hashrate' => $hashrate ];
}
$datasets = [];
$n = 0;
foreach($p as $key => $value) {
$colors = [
"0, 188, 212",
"244, 67, 54",
"255, 152, 0"
];
$datasets[] = $this->chartData('Hashrate: ' . $key, array_reverse($value['hashrate']), $colors[$n++]);
}
dd($time);
$chart = app()->chartjs
->name('lineChartTest')
->type('line')
->size(['width' => '100%', 'height' => '20%'])
->labels(array_reverse($time))
->datasets($datasets)
->optionsRaw("{
scales: {
yAxes: [{
display: true,
ticks: {
suggestedMin: 0, // minimum will be 0, unless there is a lower value.
beginAtZero: true // minimum value will be 0.
}
}]
}
}");
return $chart;
}
public function chartData($label, $data, $color)
{
return [
"label" => $label,
'backgroundColor' => "rgba(" . $color . ", 0)",
'borderColor' => "rgba(" . $color . ", 0.7)",
"pointBorderColor" => "rgba(" . $color . ", 0, 0.7)",
"pointBackgroundColor" => "rgba(" . $color . ", 0.7)",
"pointHoverBackgroundColor" => "#fff",
"pointHoverBorderColor" => "rgba(220,220,220,1)",
'data' => $data,
];
}
EDIT: I have found the answer, check below.
I've made an array $timeLabels this array contains timestamps from the current time till x minutes/hours ago in intervals of x minutes.
Then I loop over my rigs and compare the created_at with the values in the timeLabels. If it is in the same range (+/-10 sec) the data from the collection goes into an array with the timeLabel as key. When a timeLabel has no value, it gets 0 assigned as data. This actually works, but needs some tuning.
Full example:
<?php
namespace App\Http\Controllers\Rigs;
use Auth;
use Carbon\Carbon;
use App\MinerStatistics;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class RigController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the rigs dashboard.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
// Get the statistics from the database reported by the miner
// Group them by the rig name
$stats = MinerStatistics::where('user_id', Auth::user()->id)
->where('created_at','>=', Carbon::now()->subMinutes(60))
->orderBy('created_at', 'desc')
->take(300)
->get()
->groupBy('rigname');
// Get the rig names
$rigs = (object) [];
foreach($stats as $key => $value) {
$rigs->$key = $key;
}
// For each rig, get the last known stats between now and 10 seconds ago
$table = (object) [];
foreach($rigs as $key => $value) {
$tableData = $stats[$value]
->where('created_at','>=', Carbon::now()->subSeconds(10));
if (isset($tableData[0]) && $tableData !== []) {
$table->$key = (object) $tableData[0];
$table->$key->data = json_decode($tableData[0]['data'], true);
}
}
return view('rigs', [
'rigs' => $table,
'chart' => $this->chart($stats)
]);
}
public function chart($data)
{
$start = Carbon::now('Europe/Brussels');
$end = Carbon::now('Europe/Brussels')->subMinutes(60);
$timeLabels = $this->createTimeRange($end, $start);
$chartData = (object) [];
// Go over each returned rig
foreach($data as $key => $value) {
$arr = array(); // Create an empty array to store our results
// Go over all our timeLabels
foreach(array_reverse($timeLabels) as $t => $time) {
// Create a range to match the incomming created_at and timeLabels
// Needs finetuning!!
$start = Carbon::createFromFormat('H:i', $time)->subSeconds(30)->format('H:i:s');
$end = Carbon::createFromFormat('H:i', $time)->addSeconds(30)->format('H:i:s');
foreach ($value as $k => $v) {
// Transform created_at to the same format as $start and $end
$created = Carbon::createFromFormat('Y-m-d H:i:s', $v['created_at'])->format('H:i:s');
if($created >= $start && $created <= $end) {
if(!isset($v->data['hashrate'])){
$hashrate_ = json_decode($v->data,true)['hashrate'];
} else {
$hashrate_ = $v->data['hashrate'];
}
$arr[$time] = $hashrate_;
}
}
// If the set timeLabel has no value set to 0
// Rig is not running...
if (!isset($arr[$time])) {
$arr[$time] = '0';
}
}
$chartData->$key = $arr;
}
$datasets = [];
$n = 0;
foreach($chartData as $key => $value) {
$data = [];
foreach ($value as $k => $v) {
$data[] = $v;
}
$colors = [
"0, 188, 212",
"244, 67, 54",
"255, 152, 0"
];
$datasets[] = $this->chartData('Hashrate: ' . $key, array_reverse($data), $colors[$n++]);
}
$chart = app()->chartjs
->name('lineChartTest')
->type('line')
->size(['width' => '100%', 'height' => '20%'])
->labels($timeLabels)
->datasets($datasets)
->optionsRaw("{
scales: {
yAxes: [{
display: true,
ticks: {
suggestedMin: 0, // minimum will be 0, unless there is a lower value.
beginAtZero: true // minimum value will be 0.
}
}]
}
}");
return $chart;
}
public function chartData($label, $data, $color)
{
return [
"label" => $label,
'backgroundColor' => "rgba(" . $color . ", 0)",
'borderColor' => "rgba(" . $color . ", 0.7)",
"pointBorderColor" => "rgba(" . $color . ", 0, 0.7)",
"pointBackgroundColor" => "rgba(" . $color . ", 0.7)",
"pointHoverBackgroundColor" => "#fff",
"pointHoverBorderColor" => "rgba(220,220,220,1)",
'data' => $data,
];
}
private function createTimeRange($start, $end, $interval = '5 mins', $format = '24') {
$startTime = strtotime($start);
$endTime = strtotime($end);
$returnTimeFormat = ($format == '12')?'g:i A':'G:i';
$current = time();
$addTime = strtotime('+'.$interval, $current);
$diff = $addTime - $current;
$times = array();
while ($startTime < $endTime) {
$times[] = date($returnTimeFormat, $startTime);
$startTime += $diff;
}
$times[] = date($returnTimeFormat, $startTime);
return $times;
}
}
If I do it like this, I could create a dedicated class so I can reuse this logic. The data I get returned to pass to the charts is now formatted like this: (exactly what I needed)
{#3436 ▼
+"home": array:13 [▼
"17:47" => 13.44
"17:42" => 13.417
"17:37" => 13.439
"17:32" => 13.436
"17:27" => 13.438
"17:22" => "0"
"17:17" => "0"
"17:12" => "0"
"17:07" => "0"
"17:02" => "0"
"16:57" => "0"
"16:52" => "0"
"16:47" => "0"
]
+"server": array:13 [▼
"17:47" => "0"
"17:42" => 76.373
"17:37" => 76.514
"17:32" => 76.565
"17:27" => 76.486
"17:22" => "0"
"17:17" => "0"
"17:12" => "0"
"17:07" => "0"
"17:02" => "0"
"16:57" => "0"
"16:52" => "0"
"16:47" => "0"
]
}

How to build an array in recursion?

I have the following structure array:
array:6 [▼
"593a4331b25f428814000035" => array:8 [▶]
"593a4331b25f428814000036" => array:8 [▶]
"593a4331b25f428814000037" => array:8 [▶]
"593a4331b25f428814000038" => array:8 [▼
"_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => []
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object"
]
"593a4331b25f428814000039" => array:8 [▶]
"593a4331b25f42881400003a" => array:8 [▶]
]
As you can see 3-th element of array has parent 593a4331b25f428814000037, where identificator is element in the same array.
How to put this element 593a4331b25f428814000038 inside parent in children?
In result I need to get:
"593a4331b25f428814000037" => array:8 [▼
"_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => [ 0 => array("_id" => MongoId {#238 ▶}
"object_id" => "593a4331b25f428814000034"
"parameter_id" => "59398f5ab25f424016000029"
"value" => "1"
"children" => []
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object")]
"parent_id" => "593a4331b25f428814000037"
"type" => "2"
"prefix" => "object"
]
I tried this way:
public function recursion($data){
foreach ($data as $k => $value) {
if (is_array($value['children']) && count($value['children']) > 0) {
$list[$k] = $value;
$list[$k]["children"] = $this->getChildren($all, $value['children']);
} else {
$list[$k] = $value;
}
}
return $list;
}
private function getChildren($all, $childs)
{
$list = [];
foreach ($childs as $k => $child) {
if (is_array($all[$child]['children'])) {
$tmpArray = $all[$child];
$tmpArray['children'] = $this->getChildren($all, $all[$child]['children']);
} else {
$tmpArray = $all[$child];
}
$list[] = $tmpArray;
}
return $list;
}
But it works incorrect
You can use array_reduce like:
$array = array_reduce($myArray, function ($carry, $item) {
if (empty($item['parent_id'])) {
$carry[$item['object_id']] = $item;
} else {
$carry[$item['parent_id']]['children'][] = $item;
}
return $carry;
}, []);
var_dump($array);
In this example I supposed the parent_id is empty for those items which doesn't belong to a another item. You can change that with isset in case there is no parent_id key

Search array of objects for multidimensional array values in PHP

I have following task to do, if there is any chance I would appreciate some help to make it in as efficient way as possible. I need to compare values from array of objects (which comes from Laravel Query Builder join query) with array values.
Objects consist of database stored values:
0 => array:2 [
0 => {#912
+"addition_id": 1
+"valid_from": "2015-09-13 00:00:00"
+"valid_to": "2015-09-19 00:00:00"
+"price": "0.00"
+"mode": 0
+"alias": "Breakfast"
}
1 => {#911
+"addition_id": 2
+"valid_from": "2015-09-13 00:00:00"
+"valid_to": "2015-09-19 00:00:00"
+"price": "10.00"
+"mode": 1
+"alias": "Dinner"
}
while array includes new data, being processed by my method.
0 => array:3 [
0 => array:6 [
"id" => 1
"alias" => "Breakfast"
"price" => "0.00"
"mode" => 0
"created_at" => "2015-09-12 21:25:03"
"updated_at" => "2015-09-12 21:25:03"
]
1 => array:6 [
"id" => 2
"alias" => "Dinner"
"price" => "10.00"
"mode" => 1
"created_at" => "2015-09-12 21:25:18"
"updated_at" => "2015-09-12 21:25:18"
]
2 => array:6 [
"id" => 3
"alias" => "Sauna Access"
"price" => "50.00"
"mode" => 0
"created_at" => "2015-09-12 21:25:35"
"updated_at" => "2015-09-12 21:25:35"
]
]
Now, what I need to do is to find out what position of the array was not in the object (compare id with addition_id) and return it.
Is there any way to do it without two nested foreach loops? I think it can be done somehow smart with array_filter, but I'm not really sure how to write efficient callback (beginner here).
The only way I could get around this was:
private function compareAdditions(array $old,array $new)
{
$difference = $new;
foreach($new as $key => $value) {
foreach($old as $oldEntry) {
if($oldEntry->addition_id == $value['id']) {
unset($difference[$key]);
}
}
}
return $difference;
}
But I would really like to make it without two foreach loops. Help will be very appreciated :)
This might be overkill but it uses a function i write in every project, precisely for these kind of situations :
function additionals($original, $additions) {
$nonExisiting = [];
//convert all the objects in arrays
$additions = json_decode(json_encode($additions), true);
//create an index
$index = hashtable2list($original, 'id');
for(reset($additions); current($additions); next($additions)) {
$pos = array_search(current($additions)['addition_id'], $index);
if($pos !== false) {
//We could replace the originals with the additions in the same loop and save resources
//$original[$pos] = current($additions);
} else {
$nonExisiting[] = current($additions);
}
}
return $nonExisiting;
}
function hashtable2list( $hashtable, $key ){
$array = [];
foreach($hashtable as $entry) {
if( is_array($entry) && isset($entry[$key])) {
$array[] = $entry[$key];
} elseif( is_object($entry) && isset($entry->$key) ) {
$array[] = $entry->$key;
} else {
$array[] = null;
}
}
return $array;
}

Categories