php - combining json with json_decode - php

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?

Related

JSON Structure changed, breaks methods

Currently, I have below JSON stored in my database:
{
"1": [
{
"row": "My name is Trevor"
}
],
"2": [
{
"row": "Hey there! Some other text."
}
],
"3": [
{
"row": "And more."
}
]
}
Now, the third party API that I am using have changed their output format to:
[
{
"0":"My name is Trevor"
},
{
"0":"Hey there! Some other text."
},
{
"0":"And more."
}
]
I have a PHP function that reads the array-like colums and transforms each column/row. I can call it like:
public function apply(array $table) : array
{
return $this->applyRule($table);
}
Which calls this:
public function applyRule(array $table): array
{
$out = [];
foreach ($table as $col => $rows) {
$out[$col] = array_map([$this, 'rule'], $rows);
}
return $out;
}
Which ultimately calls the parsing rule, like so:
public function rule($content) : array
{
return preg_replace($this->pattern, $this->replacement, $content);
}
However, running above gives me below error:
regexTextReplace::rule() must be of the type array, string returned
I suspect that due to the change in the JSON structure, my parsing functions no longer work.
I am not sure what needs to be changed - can someone assist me?
Edit:
So looking at the answer below, adding [$rows] instead of $rows fixes the error, but ultimately creates a nested array it seems.
If I do a die-dump like:
dd($rows);
It actually does return an array:
array:3 [▼
0 => "My name is Trevor"
1 => ""
2 => ""
]
So why is it seen as a string?
You can send $rows as an array to the rule() function, just wrapping it in []:
array_map([$this, 'rule'], [$rows]);
Then the function will receive an array, not a string.
Otherwise, you can refactor your code and use a string instead, but I can't see much advantage.

If value within Json is equal to set value then return Name [PHP]

So I have a web JSON API and I want to display certain objects from it on my site. Here's my JSON;
{
"descriptions": [
{
"name" = "Jim",
"class" = "3'
},
{
"name" = "luke",
"class" = "2"
},
{
"name" = "Mat",
"class" = "3"
}
I would like to make a list of the students in class 3. Currently, this is what I have to gather and pass the data although I'm not sure how to run the check to see if they're in the class and if so return there name.
$api = "https://students.com";
$json = json_decode(file_get_contents($api ), true);
$students= $json ["descriptions"][0]["class"];
Although that just returns 3 the first class value.
Note
I apologize if there's a similar post but I was unable to find one that answers my simple request. Possibly I'm just not looking in the right place. Thanks in advance
You can use array_filter() also.
$filteredStudent = array_filter($json["description"], function($value) {
return $value["class"] == 3;
});
http://php.net/manual/en/function.array-filter.php
foreach($json["descriptions"] as $value)
{
if($value['class'] == '3')
$names[] = $value['name'];
}
return $names;

I want to add array in my mongodb data in laravel

Hi I am new to mongodb..
My problem is to append array in mongodb data
My array
{
"_id" : ObjectId("5864f61111115810fc011111"),
"estimate" : {
"estimate_id" : 1122332,
"source_data" : {
"1" : {
"test":"test"
}
}
},
"updated_at" : ISODate("1970-01-15T10:47:01.399Z"),
"created_at" : ISODate("1970-01-15T10:41:56.623Z")
}
I want to add array in source_data like bellow
{
"_id" : ObjectId("5864f61111115810fc011111"),
"estimate" : {
"estimate_id" : 1122332,
"source_data" : {
"1" : {
"name":"nikhil"
},
"2" : {
"name":"nikhil"
}
}
},
"updated_at" : ISODate("1970-01-15T10:47:01.399Z"),
"created_at" : ISODate("1970-01-15T10:41:56.623Z")
}
I have tried below code but not working
$data = array("2"=>array("name":"nikhil"));
$sourcing = Sourcing::find('5864f61111115810fc011111');//return mongo data
$sourcing->put('estimate.source_data.2',$data );
To append one or more values to an array, the following which uses the push() method should work for you:
$data = array('name' => 'nikhil');
Sourcing::find('5864f61111115810fc011111')->push('estimate.source_data', $data);

mongodb get _id as string in find query

Here I have created a collection with a single document
db.getCollection('example').insert({"example":1});
I have tried to use Projection, and I get back the _id.
db.getCollection('example').find({"example":1},{"_id":1});
{
"_id" : ObjectId("562a6300bbc948a4315f3abc")
}
However, I need the below output as shown below.
id and not _id
ObjectId("562a6300bbc948a4315f3abc") vs "562a6300bbc948a4315f3abc"
{
"id" : "562a6300bbc948a4315f3abc"
}
Although I can process #1 and #2 on my app server(PHP based) to get the desired ouput, I am looking if there is a way to get the expected result on querying from mongo itself
MongoDB 4.0 adds the $convert aggregation operator and the $toString alias which allows you to do exactly that:
db.getCollection('example').aggregate([
{ "$match": { "example":1 } },
{ "$project": { "_id": { "$toString": "$_id" } } }
])
A main usage would most likely be though to use the _id value as a "key" in a document.
db.getCollection('example').insertOne({ "a": 1, "b": 2 })
db.getCollection('example').aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[{
"k": { "$toString": "$_id" },
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": ["$$this.k", "_id"] }
}
}
}
}]
]
}
}}
])
Which would return:
{
"5b06973e7f859c325db150fd" : { "a" : 1, "b" : 2 }
}
Which clearly shows the string, as does the other example.
Generally though there is usually a way to do "transforms" on the cursor as documents are returned from the server. This is usually a good thing since an ObjectId is a 12-byte binary representation as opposed to a 24 character hex "string" which takes a lot more space.
The shell has a .map() method
db.getCollection('example').find().map(d => Object.assign(d, { _id: d._id.valueOf() }) )
And NodeJS has a Cursor.map() which can do much the same thing:
let cursor = db.collection('example').find()
.map(( _id, ...d }) => ({ _id: _id.toString(), ...d }));
while ( await cursor.hasNext() ) {
let doc = cursor.next();
// do something
})
And the same method exists in other drivers as well ( just not PHP ), or you can just iterate the cursor and transform the content as is more likely the best thing to do.
In fact, whole cursor results can be reduced into a single object with great ease by simply adding to any cursor returning statement, when working in the shell
.toArray().reduce((o,e) => {
var _id = e._id;
delete e._id;
return Object.assign(o, { [_id]: e })
},{ })
Or for full ES6 JavaScript supporting environments like nodejs:
.toArray().reduce((o,({ _id, ...e })) => ({ ...o, [_id]: e }),{ })
Really simple stuff without the complexity of what needs to process in the aggregation framework. And very possible in any language by much the same means.
You need to use the .aggregate() method.
db.getCollection('example').aggregate([ { "$project": { "_id": 0, "id": "$_id" } } ]);
Which yields:
{ "id" : ObjectId("562a67745488a8d831ce2e35") }
or using the .str property.
db.getCollection('example').find({"example":1},{"_id":1}).map(function(doc) {
return {'id': doc._id.str }
})
Which returns:
[ { "id" : "562a67745488a8d831ce2e35" } ]
Well if you are using the PHP driver you can do something like this:
$connection = new MongoClient();
$db = $connection->test;
$col = $db->example;
$cursor = $col->find([], ["_id" => 1]);
foreach($cursor as $doc) { print_r(array("id" => $doc["_id"])); }
Which yields:
Array
(
[id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
Or using again the MongoCollection::aggregate method.
$result = $col->aggregate(array(["$project" => ["id" => "$_id", "_id" => 0]]))
Then using the foreach loop:
Array
(
[_id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
One simple solution for traversing MongoCursor on PHP side is to use Generators as well as foreach or array_map($function, iterator_to_array($cursor)).
Example:
function map_traversable(callable $mapper, \Traversable $iterator) {
foreach($iterator as $val) {
yield $mapper($val);
}
}
You can meet more at PHP documentation about generators syntax.
So, now you can use/reuse it (or similar implementation) for any propose of "projecting" your data on PHP side with any amount of mapping (just like pipeline does in aggregate) but with fewer iterations amount. And this solution is pretty convenient for OOP in a case of reusing your map functions.
UPD:
Just for your case example below:
$cursor = $db->getCollection('example')->find(["example":1],["_id":1]);
$mapper = function($record) {
return array('id' => (string) $record['_id']); //see \MongoId::__toString()
}
$traversableWithIdAsStringApplied = map_traversable($mapper, $cursor);
//...
now you can proceed with more mappings applied to $traversableWithIdAsStringApplied or use just iterator_to_array for simple array retrieving.

Replace Object inside an Array in php

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

Categories