I have one array having multiple objects (say 3 Objects), each having 3 "Key-Value" pairs.
$PredefinedResult is something like this :
[
{
"EffectiveStatusId":0,
"EffectiveStatus":"abc",
"RecordCount":0
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"def",
"RecordCount":0
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"ghi",
"RecordCount":0
}
]
I have another array of objects named $MainResult with values like :
[
{
"EffectiveStatusId":1,
"EffectiveStatus":"abc",
"RecordCount":7
},
{
"EffectiveStatusId":6,
"EffectiveStatus":"def",
"RecordCount":91
}
]
Expected Result :
I want to replace the similar objects inside $PredefinedResult with the objects from $MainResult and want result like this :
[
{
"EffectiveStatusId":1,
"EffectiveStatus":"abc",
"RecordCount":7
},
{
"EffectiveStatusId":6,
"EffectiveStatus":"def",
"RecordCount":91
},
{
"EffectiveStatusId":0,
"EffectiveStatus":"ghi",
"RecordCount":0
}
]
What I tried :
I tried with this code but it's not giving me the desired result.
$FinalResult = array_replace($PredefineResult,$MainResult);
Can anyone help me on how to get the Expected Result as mentioned above ?
There's no "built-in" function for this. You're gonna have to loop and compare each manually. array_map seems like an OK choice here:
$PredefinedResult = array_map(function($a) use($MainResult){
foreach($MainResult as $data){
if($a->EffectiveStatus === $data->EffectiveStatus){
return $data;
}
}
return $a;
}, $PredefinedResult);
DEMO: http://codepad.viper-7.com/OHBQK8
Iterate through the array and manual compare the values as follows.
$res = array();
foreach ($PredefineResult as $result){
foreach ($MainResult as $mresult){
if(($result->EffectiveStatus == $mresult->EffectiveStatus) && $mresult->RecordCount!=0){
$res[] = $mresult;
}else $res[] = $result;
}
}
print_r($res);
Related
I have a very large object that I have to loop through to get a specific object.
I tried foreach($obj as $item), foreach($obj as $item => $value) and for in various configurations.
I want to get all the objects where the class is "table-responsive"
My Element looks like this (in JSON):
{
"dummy":"dummytext",
"children": [
{
"tag":null
},
{
"tag":"body",
"children": [
{
"class":"not_the_one"
},
{
"class":"table-responsive",
"html":"Gotcha!"
}
]
}
]
}
What I want to get as a result is:
{
"class":"table-responsive",
"html":"Gotcha!"
}
There could be more than one object that has the class of "table-responsive", so if there are multiple ones:
[
{
"class":"table-responsive",
"html":"Gotcha!"
},
{
"class":"table-responsive",
"html":"Gotcha!"
}
]
I was trying to accomplish a function that goes through all the elements and checks where the key of class equals "table-responsive" if so, push the object to an array. (array_push($result, $obj);) If the current element is an array, loop through it too. There can be a lot of dimensions so ideally the function calls itself. (recursive)
What you're after is a recursive reduce operation. Something that can
Iterate your elements
Capture any with the matching property, and
Perform the same operation on any children
// decode your JSON into an associative array
$data = json_decode($json, true);
// setup search parameters
$findKey = 'class';
$findValue = 'table-responsive';
// create a recursive reducer
$reducer = function($carry, $item) use (&$reducer, $findKey, $findValue) {
if (array_key_exists($findKey, $item) && $item[$findKey] === $findValue) {
// if this $item matches, add it to the $carry array
$carry[] = $item;
}
if (!empty($item['children'])) {
// this $item has children so dive deeper
return array_reduce($item['children'], $reducer, $carry);
}
// no children in this one so on to the next iteration
return $carry;
};
// Run the reducer at the top level.
// Note I've wrapped the root object in an array so the reducer can iterate it
$found = array_reduce([ $data ], $reducer, []);
// and display
echo json_encode($found, JSON_PRETTY_PRINT);
Demo ~ https://3v4l.org/6mWmC
So I have a nested array, that mimics a table layout (columns and rows):
{
"1": [
{
"row": "My name is Trevor\n"
},
{
"row": "Can you see me?\n"
},
{
"row": "\f"
}
],
"2": [
{
"row": Hey there! Some other text.\n"
},
{
"row": "What is up?\n"
},
{
"row": "\f"
}
],
"3": [
{
"row": "Some text on the third column. First row."
},
{
"row": "\f"
}
]
}
So "1", "2", "3" are the columns and then under each column, there can be any number of rows.
Now I am trying to do, so my users can perform various parsing rules on either:
All columns and all rows.
Specific columns and all rows.
Whenever a column / row has been parsed, it should be returned to the "original array".
For this, I have created a class that will apply the different parsing rules I have in specified. Getting the parsing rule works fine. I am currently stuck in the actual text transformation/parsing aspect.
Consider I have a parsing rule called "regexTextReplace", that looks like this:
class regexTextReplace
{
private $pattern;
private $replacement;
public function __construct(array $arguments)
{
$this->pattern = $arguments['pattern'];
$this->replacement = $arguments['replacement'];
}
public function apply(array $table, $column = false): array
{
$table = $column ? $table[$column] : $table;
return array_map('self::regex_replace', $table);
}
public function regex_replace(array $table)
{
return preg_replace($this->pattern, $this->replacement, $table);
}
}
This is how I'm using it:
$options = [
'pattern' => '/Trevor/i',
'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
$columns = $engine->apply($document->content, 1); //"1" is the specific column.
$columns returns:
[
{
"row": "My name is Oliver\n"
},
{
"row": "Can you see my?\n"
},
{
"row": "\f"
}
]
Two problems here:
It successfully apply the parsing rule (Trever is replaced with Oliver). But it only returns the first column, but I want the entire original array to be transformed.
If I remove the 1 from the apply() method, I get below error:
Array to string conversion
on below line:
return preg_replace($this->pattern, $this->replacement, $table);
Can anyone guide me in the right direction, so I can perform my parsing rule on any column or on all columns, and return the transformed data back to my original array?
I would rewrite the apply function to loop over the entire table, processing each column if the column argument is not set, or if it matches the current table column:
public function apply(array $table, $column = false): array
{
$out = array();
foreach ($table as $col => $rows) {
if ($column === false || $col == $column) {
$out[$col] = array_map('self::regex_replace', $rows);
}
else {
$out[$col] = $rows;
}
}
return $out;
}
Demo on 3v4l.org
You could rewrite your apply method to this:
public function apply(array $table, $columns = false): array
{
$columns = $columns === false ? array_keys($table) : (array)$columns;
return array_map(function ($column) use ($table, $columns) {
return in_array($column, $columns) ? array_map('self::regex_replace', $table[$column]) : $table[$column];
}, array_keys($table));
}
You can pass either a single column, or an array of columns, or nothing (false) to specify the columns you want adjusted.
Demo: https://3v4l.org/Kn4FY
Just loop it and do the regex on all subarrays:
$content = json_decode($json, true);
$options = [
'pattern' => '/Trevor/i',
'replacement' => 'Oliver',
];
$engine = new regexTextReplace($options);
foreach($content as $key => $v){
$columns[$key] = $engine->apply($content, $key);
}
var_dump($columns);
Working demo:
https://3v4l.org/Pk2rC
The benefit of looping in the "PHP" side instead of in the class is that you can still apply the regex to only one or two of the subarrays.
If you loop in the class then you need to pass more arguments to restrict the looping or do some type of array slicing.
I couldn't find an answer, so I decided to ask.
I get this response from an API:
[
{
"seasonNumber":1,
"numWins":1,
"numHighBracket":2,
"numLowBracket":2,
"seasonXp":111,
"seasonLevel":5,
"bookXp":0,
"bookLevel":1,
"purchasedVIP":false
},
{
"seasonNumber":2,
"numWins":1,
"numHighBracket":21,
"numLowBracket":31,
"seasonXp":1651,
"seasonLevel":25,
"bookXp":9,
"bookLevel":11,
"purchasedVIP":false
},
{
"seasonNumber":3,
"numWins":9,
"numHighBracket":57,
"numLowBracket":127,
"seasonXp":4659,
"seasonLevel":68,
"bookXp":0,
"bookLevel":100,
"purchasedVIP":true
},
{
"seasonNumber":4,
"numWins":8,
"numHighBracket":19,
"numLowBracket":36,
"seasonXp":274,
"seasonLevel":33,
"bookXp":7,
"bookLevel":35,
"purchasedVIP":true
}
]
I am trying to change the json data to this:
{
"seasons":
[
{
"season":1,
"battle_pass":false
},
{
"season":2,
"battle_pass":false
},
{
"season":3,
"battle_pass":true
},
{
"season":4,
"battle_pass":true
}
]
}
In my current code I am using regex like this:
preg_match_all("/(?:\{\"seasonNumber\"\:(\w)|purchasedVIP\"\:(\w+))/", $response, $seasons);
echo '{"seasons":'.json_encode($seasons, JSON_FORCE_OBJECT, JSON_PRETTY_PRINT).'}';
It's basically putting everything in a separate array but that's not what I want.
Decode the json, restructure the data, re-encode.
Code: (Demo)
// your $json =
foreach (json_decode($json) as $set) {
$array[] = ["season" => $set->seasonNumber, "battle_pass" => $set->purchasedVIP];
}
echo json_encode(["seasons" => $array]);
Output:
{"seasons":[{"season":1,"battle_pass":false},{"season":2,"battle_pass":false},{"season":3,"battle_pass":true},{"season":4,"battle_pass":true}]}
p.s. if you want to force objects and pretty print, separate those flags with a pipe (|). https://3v4l.org/qsPb0
I have this problem where an API responds to me with DEPARTURESEGMENT sometimes containing only one object, and sometimes containing an array of objects. Depending on which case it is, I seem to need different logics in my foreach-loop.
Response A:
{
"getdeparturesresult":{
"departuresegment":[{
"departure":{
"location":{
"#id":"7461018",
"#x":"12.523958",
"#y":"57.938402",
"name":"Noltorps centrum"
},
"datetime":"2014-12-04 23:05"
},
"direction":"Alingsås station",
"segmentid":{
"mot":{
"#displaytype":"B",
"#type":"BLT",
"#text":"Buss"
},
"carrier":{
"name":"Västtrafik",
"url":"http://www.vasttrafik.se/",
"id":"279",
"number":"1"
}
}
},
{
"departure":{
"location":{
"#id":"7461018",
"#x":"12.523958",
"#y":"57.938402",
"name":"Noltorps centrum"
},
"datetime":"2014-12-04 23:05"
},
"direction":"Alingsås station",
"segmentid":{
"mot":{
"#displaytype":"B",
"#type":"BLT",
"#text":"Buss"
},
"carrier":{
"name":"Västtrafik",
"url":"http://www.vasttrafik.se/",
"id":"279",
"number":"1"
}
}
}
]
}
}
Works with this loop:
foreach ($apiData->getdeparturesresult->departuresegment as $m) {
While this response B:
{
"getdeparturesresult":{
"departuresegment":{
"departure":{
"location":{
"#id":"7461018",
"#x":"12.523958",
"#y":"57.938402",
"name":"Noltorps centrum"
},
"datetime":"2014-12-04 23:05"
},
"direction":"Alingsås station",
"segmentid":{
"mot":{
"#displaytype":"B",
"#type":"BLT",
"#text":"Buss"
},
"carrier":{
"name":"Västtrafik",
"url":"http://www.vasttrafik.se/",
"id":"279",
"number":"1"
}
}
}
}
}
needs a loop like this (otherwise it throws an error):
foreach ($apiData->getdeparturesresult as $m) {
Is there a way to write the loop failsafe for whether DEPARTURESEGMENT is an array of objects or just one object (the brackets [] is the only difference to the structure of the json right?) or do I have to somehow test and see first whether DEPARTURESEGMENT is an array or not, and dispatch to two different loops depending on the outcome?
You have a few methods that can help you:
is_array
is_object
instanceof // if you receive specific object
gettype
json_decode second parameter, which if is set to true, tries to decode the json as an array
In you situation, you would probably be fine by doing the following:
if (is_object($entry)) {
handleObject($entry);
} elseif (is_array($entry) && count($entry)) {
foreach ($entry as $e) {
handleObject($e);
}
}
I have this little useful function in my standard repertoire:
function iter($x) {
if(is_array($x))
return $x;
if(is_object($x)) {
if($x instanceof \Iterator)
return $x;
if(method_exists($x, 'getIterator'))
return $x->getIterator();
return get_object_vars($x);
}
return array($x);
}
This way you can use any variable with foreach without having to check it beforehand:
foreach(iter($whatever) as $item)
...
How about checking whether it's an array or not with is_array?
I made a simple example of it's usage here - http://codepad.org/WNjbIPZF
I'm trying to combine multiple JSON objects into a single one in PHP. I'm iterating through the JSON objets, decoding them, parsing out the parts I want to keep, and storing them in a property in my php class.
Supposing my json objects look like the following format:
{
"lists" : {
"list" : [
{
"termA" : 2 ,
"termB" : "FOO"
}
]
}
}
I want to eventually combine everything into a JSON object like so.
{
"lists" : {
"list" : [
{
"termA" : 2 ,
"termB" : "FOO"
},
{
"termA" : 2 ,
"termB" : "FOO"
}
]
} ,
"lists" : {
"list" : [
{
"termA" : 4 ,
"termB" : "BAR"
},
{
"termA" : 4 ,
"termB" : "BAR"
}
]
}
}
I'm trying to store Arrays in a property within my class in a function that gets called iteratrivley:
private function parseData($json){
$decodeData = json_decode($json);
$list = $decodeData->lists;
$this->output .= $list
}
However I get the following error during the "$this->output .= $list" line.
Object of class stdClass could not be converted to string
Right now $this->output has no initial value. What might be the best way to store the "list" arrays temporarily, and then reformat them after going through all of the json objects?
Thanks.
You were close:
private function parseData($json){
$decodeData = json_decode($json);
$list = $decodeData['lists'];
$this->output .= $list
}
{
"lists" : {
...
} ,
"lists" : {
...
}
}
That's not valid/meaningful JSON. You have a hash with the same key (lists) in it twice. How would you address that?