PHP change a function from from loops to recursion - php

I have the following JSON object:
{
"id": 1,
"name": null,
"block": {
"type": "none",
"descends": [
{
"operation":"sum",
"descends":[
{
"label":2,
"value":false
}
]
},
{
"label": 1,
"value": 3,
},
{
"label": 2,
"value": 2
}
],
"label": 1,
"value": true
}
}
I want to collect all the label and value attributes and store them in an array, so I created the following function:
public function collectValues($arr){
$finalValues = [];
foreach($arr as $key => $element){
if($key=='block'){
foreach($element as $key2 => $block){
if($key2=='descends'){
foreach($block as $key3 => $node_block){
if($key3=='descends'){
foreach($node_block as $key4 => $anotherNode){
if($key4 == 'descends'){
foreach($anotherNode as $finalNode){
$finalValues [] = array('lable'=>$finalNode->label, 'value' =>$finalNode->value);
}
}
}
}
else{
$finalValues [] = array('lable' => $node_block->label, 'value' => $node_block->value);
}
}
}
}
$finalValues [] = array('lable'=> $element->label, 'value' => $element->value);
}
}
return $finalValues;
}
The function works and I get the following:
[
{
"lable": 2,
"value": false
},
{
"lable": 1,
"value": 3
},
{
"lable": 2,
"value": 2
},
{
"lable": 1,
"value": true
}
]
The problem is that the JSON object can contain more descends like:
{
"id": 1,
"name": null,
"block": {
"type": "none",
"descends": [
{
"operation":"sum",
"descends":[
{
"operation":"sum",
"descends":[
{
"label":2,
"value":false
}
],
"label":2,
"value":false
}
]
},
{
"operation":"sum",
"descends":[
{
"label":2,
"value":false
}
],
"label": 1,
"value": 3,
},
{
"label": 2,
"value": 2
}
],
"label": 1,
"value": true
}
}
This means I will have to add more foreach loops. A good way to handle such a situation is by using recursive. How can I transform the function above into a recursive one?

Your looped function does not make sense to change. Easier to rewrite from scratch
Here is my use case for recursion. Maybe there is an easier option, but I didn’t chase optimization
$array = json_decode($json, true); //in $json your JSON)
$result = []; // array for result
checkLabel($array, $result);
print_r($result);
function checkLabel(array $array, &$result){
//first check every element on nested array
foreach ($array as $key => $value){
if (is_array($value)) {
//if found call recursive function
checkLabel($value, $result);
}
}
//then check 'label' key.. its mean we have attribute
if (array_key_exists('label', $array)) {
//save it
$result[] = [
'label' => $array['label'],
'value' => $array['value']??'' //if have label but without value)
];
}
}

Related

Convert JSON IN LARAVEL

Here is my JSON
[
{
"TIMESTAMP": "2021-06-09 13:13:26",
"COL1": "10",
"COL2": "20",
"COL3": "30"
},
{
"TIMESTAMP": "2021-06-22 13:13:26",
"COL1": "20",
"COL2": "30",
"COL3": "40"
},
{
"TIMESTAMP": "2021-06-21 13:13:26",
"COL1": "1",
"COL2": "2",
"COL3": "3"
},
{
"TIMESTAMP": "2021-06-20 13:13:26",
"COL1": "40",
"COL2": "50",
"COL3": "60"
}
]
I need to refactor the json According to the Column name like (EXPECTED OUTPUT)
[
{
"TITLE":"COL1"
"DATA":[10,20,1,40]
},
{
"TITLE":"COL2"
"DATA":[20,30,2,50]
},
{
"TITLE":"COL3"
"DATA":[30,40,3,60]
},
]
I was tried but it not working
$data = json_decode($result, true);
$refactored = array_map(function($item) {
return (object)[
'TIMESTAMP' => $item['TIMESTAMP'],
'DATA' => [ $item['COL1'], $item['COL2'], $item['COL3'] ]
];
}, $data);
dump($refactored);
Someone help me out with this. The column may be 3 or more and it must be dynamic. Thanks in advance.
You can transform your JSON like this:
$data = json_decode($result, true);
$refactored = array_reduce($json, function($carry, $item) {
foreach($item as $key => $value) {
if (str_starts_with($key, 'COL')) {
$index = substr($key, 3, 1) - 1;
if (!isset($carry[$index])) {
$carry[$index] = [
'Title' => $key
];
}
$carry[$index]['Data'][] = $value;
}
}
return $carry;
}, []);
dump($refactored);
I am always a fan of using the value you want to group your data by as a temporary array key, that makes things easier, and can simply be reset by using array_values afterwards.
$input = json_decode('…', true);
$output = [];
foreach($input as $item) {
foreach($item as $key => $value) {
if($key != 'TIMESTAMP') {
$output[$key]['TITLE'] = $key;
$output[$key]['DATA'][] = (int)$value;
}
}
}
$output = array_values($output);
echo json_encode($output);
All you have to do is re-encode the processed data:
json_encode($refactored) will give you the output that you want.
P.S. you don't have to cast to an object. It works as array as well.

Parse Json key/values of a key's value using JObject using PHP

Please can any help me on this, to remove all keys that have values of N/A, -, or empty strings. If one of the values appear in an array then remove that single item from the array
{
"name": {
"first": "Robert",
"middle": "",
"last": "Smith"
},
"age": 25,
"DOB": "-",
"hobbies": [
"running",
"coding",
"-"
],
"education": {
"highschool": "N/A",
"college": "Yale"
}
}
Expecting result would be:
{
"name": {
"first": "Robert",
"last": "Smith"
},
"age": 25,
"hobbies": [
"running",
"coding"
],
"education": {
"highschool": "N/A",
"college": "Yale"
}
}
You could use following logic to remove elements from your source data:
The recursive function cleanUp() cycles through the data and removes all the entries with values that are set in array $remove
$arr = json_decode($json, true);
$remove = ['N/A', '-','',];
cleanUp($arr, $remove);
function cleanUp(array &$arr, array $remove = [])
{
foreach($arr as $key => &$value) {
if(is_array($value)) {
cleanUp($value, $remove);
} else {
if(in_array($value, $remove)) unset($arr[$key]);
}
}
}
working demo

merge array keys and add the values-PHP

I want to merge two same keys in an array and get the sum of the values.
I want the same structure as it is now.Because this data needs to be converted to JSON.
This is what i get now.
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 9,
"user": "Student",
"id": "45"
},
{
"count_of_invites": 4,
"user": "Student",
"id": "45"
}
]
}
As you can see the id 45 are repeated.As i want the result as,
Expected output
{
"data": [{
"count_of_invites": 5,
"user": "Rajesh",
"id": "53"
},
{
"count_of_invites": 13,
"user": "Student",
"id": "45"
}
]
}
As you can see the duplicate entry should be removed as well as the count_of_invites of duplicate entry should be added.
<?php
$data = [
[
'id' => 2,
'name' => 'Paul',
'count' => 4
],
[
'id' => 3,
'name' => 'Peter',
'count' => 5
],
[
'id' => 3,
'name' => 'Peter',
'count' => 7
]
];
foreach($data as $array)
$counts[$array['id']][] = $array['count'];
$counts = array_map('array_sum', $counts);
foreach($data as $k => $array)
$data[$k]['count'] = $counts[$array['id']];
$data = array_unique($data, SORT_REGULAR);
print json_encode($data, JSON_PRETTY_PRINT);
Output:
[
{
"id": 2,
"name": "Paul",
"count": 4
},
{
"id": 3,
"name": "Peter",
"count": 12
}
]
You can achieve it this way:
$ids = array();
$output = array();
foreach ($input as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]]=$count($output);
$output[]=$value;
} else {
$output[$ids[$value["id"]]]["count_of_invites"] = $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}
The count method was declared as variable and i've added with addition assignment operator.
Thank You for helping.
$ids = array();
$output = array();
foreach ($response as $value) {
if (!isset($ids[$value["id"]])) {
$ids[$value["id"]] = count($output);
$output[] = $value;
}
else {
$output[$ids[$value["id"]]]["count_of_invites"] += $value["count_of_invites"];
$output[$ids[$value["id"]]]["user"] = $value["user"];
}
}

Php - creating json object

I am trying to create a json object from the data that I get from Ninja Forms that would look like this:
{
"title": "Contact Me",
"fields": [
{
"label": "Name",
"type": "textbox",
"required": "1"
},
{
"label": "Email",
"type": "email",
"required": "1"
}
]
}
I am trying to do so, like this:
$settings = ['label', 'type', 'required'];
$formTitle = Ninja_Forms()->form( 1 )->get()->get_setting('title');
$formFields = Ninja_Forms()->form(1)->get_fields();
$data = ['title' => $formTitle];
foreach ($formFields as $formField) {
$key = $formField->get_setting('key');
foreach ($settings as $setting) {
$data['fields'][$key][][$setting] = $formField->get_setting($setting);
}
}
return $data;
But, the result of that looks like this:
{
"title": "Contact Me",
"fields": {
"name": [
{ "label": "Name" },
{ "type": "textbox" },
{ "required": "1"}
],
"email": [
{ "label": "Email" },
{ "type": "email" },
{ "required": "1" }
],
How can I do this, so that the result looks like the one I have shown above?
I have also tried like this:
foreach ($settings as $setting) {
$data['fields'][] = $formField->get_setting($setting);
}
But, that gave me this kind of result:
{
"title": "Contact Me",
"fields": [
"Name",
"textbox",
"1",
"Email",
"email",
"1",
"Message",
"textarea",
"1",
"Submit",
"submit",
null
]
}
This gave me the wanted result:
foreach ($formFields as $formField) {
$key = $formField->get_setting('key');
foreach ($settings as $setting) {
$object[$setting] = $formField->get_setting($setting);
}
$data['fields'][] = $object;
}
return $data;
One way to look at this is to count the dimensions of the data. In your desired format, the deepest item is:
{ "fields": [ { "label": "Name"
So you have object -> array -> object.
If we indent each array in your code, we have:
$data // outermost array
['fields']
[$key]
[] // innermost array
[$setting] = $value; // key in innermost array
Or if we were to declare it with just one value:
$data = array(
'field' => array(
$key => array(
0 => array(
$setting => $value
)
)
)
);
So you have 4 levels of array, instead of 3.
Comparing to the JSON, taking an array with numeric keys as "array" and one with non-numeric keys as "object", the pattern is object -> object -> array -> object.
So it's the [$key] we need to eliminate, because it's creating an extra object dimension.
But we don't want to increment the key at [] for each item either, so we need to either make our value in advance...
foreach ($settings as $setting) {
$object[$setting] = $formField->get_setting($setting);
}
$data['fields'][] = $object;
...or choose our key in advance:
$i++;
foreach ($settings as $setting) {
$data['fields'][$i] = $formField->get_setting($setting);
}

PHP - Nested array keys based on string lines

I need a help. I'm looking for a solution for this problem!
I have a file which contains the following pattern:
Brazil|SaoPaulo|Diadema|RuadaFe Brazil|SaoPaulo|Diadema|RuadoLimoeiro
Brazil|SaoPaulo|SaoCaetano|RuadasLaranjeiras
Brazil|Parana|Curitiba|ComendadorAraujo
USA|NewJersey|JerseyCity|WhashingtonBoulervard
USA|NewJersey|JerseyCity|RiverCourt
Which should bring after some array key implementation, something like this (after apply json_encode call on php):
{
"name": "Brazil",
"children": [
{
"name": "SaoPaulo",
"children": [
{
"name": "Diadema",
"children": [
{"name": "RuadaFe"},
{"name": "RuadoLimoeiro"}
]
},
{
"name": "SaoCaetano",
"children": [
{"name": "RuadasLaranjeiras"}
]
},
]
"name": "Parana",
"children": [
{
"name": "Curitiba",
"children": [
{"name": "ComendadorAraujo"}
]
}
]
},
"name":"USA",
"children":[
{
"name": "NewJersey",
"children": [
{
"name": "JerseyCity",
"children": [
{"name": "WhashingonBoulevard"},
{"name": "RiverCourt"}
]
}
]
}
]
}
]
And keep going and going (and even more deeper too).
Please, help me team... thanks in advance.
Here what I get until now:
Array
(
[Brazil] => Array
(
[SaoPaulo] => Array
(
[Diadema] => Array
(
[RuadoLimoeiro] =>
)
[SaoCaetano] => Array
(
[RuadasLaranjeiras] =>
)
)
[Parana] => Array
(
[Curitiba] => Array
(
[ComendadorAraujo] =>
)
)
)
[USA] => Array
(
[NewJersey] => Array
(
[JerseyCity] => Array
(
[WhashingtonBoulervard] =>
[RiverCourt] =>
)
)
)
)
And here is the json encoded:
{
"Brazil":{
"SaoPaulo":
{"Diadema":
{"RuadoLimoeiro":null},
"SaoCaetano":{"RuadasLaranjeiras":null}
},
"Parana":
{"Curitiba":
{"ComendadorAraujo":null}
}
},
"USA":{
"NewJersey":{
"JerseyCity":{
"WhashingtonBoulervard":null,
"RiverCourt":null}
}
}
}
As you can see, the "names" and "child" is missing because is not an array key structure, also something is wrong, because I'm missing some values on SaoPaulo.
Here is the function:
foreach($strings as $string) {
$parts = array_filter(explode('|', $string));
$ref = &$result;
foreach($parts as $p) {
// echo $p;
if(!isset($ref[$p])) {
$ref[$p] = array();
// $ref[$p] = array("name"=>$p);
}
$ref = &$ref[$p];
}
$ref = null;
}
-------------------------------- AFTER SOME ANSWERS --------------------------
{
"name": "Brazil(country)",
"children": [
{
"name": "SaoPaulo(state)", // only one state
"children": [
{
"name": "Diadema(city)", // only one city
"children": [
{"name": "RuadaFe(street)"}, // two streets under the same city...
{"name": "RuadoLimoeiro(street)"}
]
},
{
"name": "SaoCaetano(city)",
"children": [
{"name": "RuadasLaranjeiras(street)"}
]
},
]
"name": "Parana(state)",
"children": [
{
"name": "Curitiba(city)",
"children": [
{"name": "ComendadorAraujo(street)"}
]
}
]
},...
I put on parentesis the structure (country, state, city, street) just to clarify what i want.
Got it?
It is not outright trivial, but what I did is actually the same as you (the same way as in your edited question), but I also keep track of those arrays to rename the keys. And that's what I do afterwards then:
$separator = [' ', '|'];
$buffer = file_get_contents($file);
$entries = explode($separator[0], $buffer);
$result = [];
$named[] = &$result;
########
foreach ($entries as $entry) {
$each = explode($separator[1], $entry);
$pointer = & $result;
while ($current = array_shift($each)) {
if (!isset($pointer[$current])) {
unset($children);
$children = [];
$named[] = &$children;
########
$pointer[$current] = ['name' => $current, 'children' => &$children];
}
$pointer = & $pointer[$current]['children'];
}
}
foreach($named as $offset => $namedArray) {
$keys = array_keys($namedArray);
foreach($keys as $key) {
$named[$offset][] = &$namedArray[$key];
unset($named[$offset][$key]);
}
}
print_r($result);
See it in action.
You probably might want to modify this by checking in the later foreach if children are empty (leaf-nodes) and then removing the children entry as well.
Here the modified foreach at the end and json output:
foreach($named as $offset => $namedArray) {
$keys = array_keys($namedArray);
foreach($keys as $key) {
if (!$namedArray[$key]['children']) {
unset($namedArray[$key]['children']);
}
$named[$offset][] = &$namedArray[$key];
unset($named[$offset][$key]);
}
}
echo json_encode($result, JSON_PRETTY_PRINT);
Output:
[
{
"name": "Brazil",
"children": [
{
"name": "SaoPaulo",
"children": [
{
"name": "Diadema",
"children": [
{
"name": "RuadaFe"
},
{
"name": "RuadoLimoeiro"
}
]
},
{
"name": "SaoCaetano",
"children": [
{
"name": "RuadasLaranjeiras"
}
]
}
]
},
{
"name": "Parana",
"children": [
{
"name": "Curitiba",
"children": [
{
"name": "ComendadorAraujo"
}
]
}
]
}
]
},
{
"name": "USA",
"children": [
{
"name": "NewJersey",
"children": [
{
"name": "JerseyCity",
"children": [
{
"name": "WhashingtonBoulervard"
},
{
"name": "RiverCourt"
}
]
}
]
}
]
}
]
The full code-example at a glance:
<?php
/**
* PHP - Nested array keys based on string lines
* #link http://stackoverflow.com/a/16305236/2261774
*/
$file = 'data://text/plain;base64,' . base64_encode('Brazil|SaoPaulo|Diadema|RuadaFe Brazil|SaoPaulo|Diadema|RuadoLimoeiro Brazil|SaoPaulo|SaoCaetano|RuadasLaranjeiras Brazil|Parana|Curitiba|ComendadorAraujo USA|NewJersey|JerseyCity|WhashingtonBoulervard USA|NewJersey|JerseyCity|RiverCourt');
$separator = [' ', '|'];
$buffer = file_get_contents($file);
$entries = explode($separator[0], $buffer);
$result = [];
$named[] = &$result;
foreach ($entries as $entry) {
$each = explode($separator[1], $entry);
$pointer = & $result;
while ($current = array_shift($each)) {
if (!isset($pointer[$current])) {
unset($children);
$children = [];
$named[] = &$children;
$pointer[$current] = ['name' => $current, 'children' => &$children];
}
$pointer = & $pointer[$current]['children'];
}
}
unset($pointer);
foreach($named as $offset => $namedArray) {
$keys = array_keys($namedArray);
foreach($keys as $key) {
if (!$namedArray[$key]['children']) {
unset($namedArray[$key]['children']);
}
$named[$offset][] = &$namedArray[$key];
unset($named[$offset][$key]);
}
}
unset($named);
echo json_encode($result, JSON_PRETTY_PRINT);
your question JSON:
{
"name": "Brazil",
"children": [
{
"name": "SaoPaulo",
"children": [
{
"name": "Diadema",
"children": [
{"name": "RuadaFe"},
{"name": "RuadoLimoeiro"}
]
},
My Answer JSON:
[
{
"name": "Brazil",
"children": [
{
"name": "SaoPaulo",
"children": [
{
"name": "Diadema",
"children": [
{
"name": "RuadaFe"
},
{
"name": "RuadoLimoeiro"
}
]
},
The only difference I can spot is that the root-nodes are wrapped inside an array in my answer, but it should be trivial to fetch them out there if that really is your issue ;)
Does it solves your problem? Use $inputString to put your real string.
<?php
// ------------------------------------------------------ your input goes here ------------------------------------------------------
$inputString = 'Brazil|SaoPaulo|Diadema|RuadaFe Brazil|SaoPaulo|Diadema|RuadoLimoeiro Brazil|SaoPaulo|SaoCaetano|RuadasLaranjeiras Brazil|Parana|Curitiba|ComendadorAraujo USA|NewJersey|JerseyCity|WhashingtonBoulervard USA|NewJersey|JerseyCity|RiverCourt';
class item {
public $name = null;
public function getChildrenByName($strName) {
$ret = null;
# this variable should be defined in interface, but i skipped it so it wont be printed in json when obj does not have childrens
if( !isset( $this->children ) ) {
$this->children = array( );
}
foreach ( $this->children as $child ) {
if( $child->name === $strName ) {
$ret = $child;
break;
}
}
if ( !$ret ) {
$this->children[] = self::spawnByName( $strName );
}
return $this->children[ count($this->children) - 1];
}
static public function spawnByName($strName) {
$ret = new item();
$ret->name = $strName;
return $ret;
}
}
class listManager {
protected $list = array();
public function getList() {
return $this->list;
}
public function addPath( $desiredPath ) {
# path needs to be as array
if ( is_string( $desiredPath ) ) {
$desiredPath = explode('|', $desiredPath);
}
# create root element if it does not already exists
if ( !isset( $this->list[$desiredPath[0]] ) ) {
$this->list[$desiredPath[0]] = item::spawnByName($desiredPath[0]);
}
$curElement = $this->list[$desiredPath[0]];
for( $i=1; $i<count($desiredPath); $i++ ) {
$curElement = $curElement->getChildrenByName( $desiredPath[$i] );
}
}
protected function spawnElement( $strName ) {
$ret = new item();
$ret->name = $strName;
return $ret;
}
}
$output = array();
$expl = explode(' ', $inputString);
$list = new listManager();
foreach ( $expl as $key => $path ) {
$list->addPath( $path );
}
$output = '';
foreach ( $list->getList() as $singleVariable ) {
$output .= json_encode($singleVariable, JSON_PRETTY_PRINT) . ",\n";
}
echo '<pre>'.$output.'</pre>';
?>
Above code produces following json out of your sample code:
{
"name": "Brazil",
"children": [{
"name": "SaoPaulo",
"children": [{
"name": "Diadema",
"children": [{
"name": "RuadaFe"
}
]
}
]
}, {
"name": "SaoPaulo",
"children": [{
"name": "Diadema",
"children": [{
"name": "RuadoLimoeiro"
}
]
}
]
}, {
"name": "SaoPaulo",
"children": [{
"name": "SaoCaetano",
"children": [{
"name": "RuadasLaranjeiras"
}
]
}
]
}, {
"name": "Parana",
"children": [{
"name": "Curitiba",
"children": [{
"name": "ComendadorAraujo"
}
]
}
]
}
]
} {
"name": "USA",
"children": [{
"name": "NewJersey",
"children": [{
"name": "JerseyCity",
"children": [{
"name": "WhashingtonBoulervard"
}
]
}
]
}, {
"name": "NewJersey",
"children": [{
"name": "JerseyCity",
"children": [{
"name": "RiverCourt"
}
]
}
]
}
]
}
edit: changed, does it fit now?

Categories