I've got the following data structure:
Array -> Object -> Array -> Object -> Object
[
{
"id":6834,
"contract_id":13,
"schedule_column_values":[
{
"id":34001,
"field_value":{
"id":324241,
"value":10,
"field":{
"id":1,
"signature":"ios"
}
}
},
{
"id":34001,
"field_value":{
"id":324241,
"value":10,
"field":{
"id":1,
"signature":"android"
}
}
}
]
}
]
What I'm trying to achieve is that if a field has the signature of "android", remove its grandparent object from schedule_column_values. Basically, if a signature is "android", the final data would look like this:
[
{
"id": 6834,
"contract_id": 13,
"schedule_column_values": [
{
"id": 34001,
"field_value": {
"id": 324241,
"value": 10,
"field": {
"id": 1,
"signature": "ios"
}
}
}
]
}
]
This is just an example but the structure is always the same and we always know what signature we're looking for. It could be anything other than android but we know the string we're looking for.
I've tried a nested foreach loop and tried unset but it doesn't seem to work. The other way is I've set a NULL to object value of schedule_column_values when the signature of field is matched, but I cannot have NULL in the object.
What would be a good way to filter out this structure?
This is a perfect use case for array_filter:
$filtered_array = [];
foreach($array as $grandparent){
$filtered_schedules = array_filter(
$grandparent->schedule_column_values,
function($item){
return $item->field_value->field->signature !== 'android';
}
);
$altered_grandparent = clone $grandparent;
$altered_grandparent->schedule_column_values = $filtered_schedules;
$filtered_array[] = $altered_grandparent;
}
Related
I have in the process of moving some code from the front-end (in JavaScript) to the server-side (which is PHP) where it will be filtered and sent out in an API call, and I can't seem to get the filter working properly on the back-end. The code takes an array of objects and filters it for the objects where a certain nested field (which is also an array of objects) contains certain values. The basic shape of the API:
{
"id": 1217,
"name": "Best product ever",
"tags": [
{
"id": 125,
"name": "Important Value",
"slug": "important-value"
},
{
"id": 157,
"name": "Value",
"slug": "value"
},
{
"id": 180,
"name": "Value",
"slug": "value"
},
{
"id": 126,
"name": "Value",
"slug": "value"
},
{
"id": 206,
"name": "Other Important Value",
"slug": "other-important-value"
}
}
The working JS code:
let productAttributes = ['important-value', 'value', 'value', 'value', 'other-important-value'];
filterResults(results) {
let filteredResults = results.filter(product => {
return product.tags.find(tag => {
return tag.slug === this.productAttributes[0];
});
});
if (this.productAttributes[0] !== 'certain important value') {
filteredResults = filteredResults.filter(product => {
return product.tags.find(tag => {
return tag.slug === this.productAttributes[4];
});
});
}
return filteredResults;
}
And the (not yet working) PHP code:
function get_awesome_products() {
$baseRequest = 'https://myawesomeapi/wp-json/wc/v3/products/?
consumer_key=xxxx&consumer_secret=xxxx&per_page=100&page=';
for ($count = 1; $count <= 9; $count++ ) {
$request = wp_remote_get( $baseRequest . (string)$count);
$body = wp_remote_retrieve_body( $request );
$data = array_values( json_decode( $body, true ));
if ($count < 2) {
$completeProductList = $data;
} else {
$completeProductList = array_merge($completeProductList, $data);
}
}
// The code above this comment is doing what I expect, the code below is not.
$filteredProducts = null;
foreach ($completeProductList as &$product) {
$tagArray = $product['tags'];
if (in_array($reg_test_array[0], $tagArray, true) &&
in_array($reg_test_array[4], $tagArray, true))
{
array_push($filteredProducts, $product);
}
unset($product);
return new WP_REST_Response($filteredProducts, 200);
The impression I get is that I need to write a custom function to take the place of Array.prototype.find(), but I'm not strong in PHP and am having trouble wrapping my head around it.
EDIT: Edited to add example of object being filtered and additional PHP code
You could also use the PHP equivalent function array_filter (among a few other array-specific functions) for this task.
Example:
// Your 0 and 4 index values from $reg_test_array
$importantTags = [ "important-value", "other-important-value" ];
$filteredProducts = array_filter($completeProductList, function($product) use ($importantTags) {
return (bool)array_intersect($importantTags, array_column($product['tags'], 'slug'));
});
return new WP_REST_Response($filteredProducts , 200);
Sandbox
This should be equivalent to the JavaScript code you posted, but done without looping through the filtered results twice.
Without knowing the context of important-value and other-important-value, and how they come to be ordered in the $attributes array, it's a little difficult to improve upon the conditional checks used. What I've written thus far however feels like a code smell to me, because it's reliant hard coded values.
function filterResults(array $results, array $attributes)
{
return array_reduce($results, function ($filteredResults, $result) use ($attributes) {
// Extract tag slugs from result
$tagSlugs = array_column($result['tags'], 'slug');
// Append result to filtered results where first attribute exists in tag slugs;
// Or first attribute is not *other-important-value* and fourth attribute exists in tag slugs
if (in_array($attribute[0], $tagSlugs) && ($attribute[0] === 'other-important-value' || in_array($attribute[4], $tagSlugs))) {
$filteredResults[] = $result;
}
return $filteredResults;
}, []);
}
I'm having a lot of trouble with serializing collection from which I've removed first element.
I have CompaniesCollection entity with Many2Many relation to Company entity.
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Company")
* #Groups({"get-by-collection-owner"})
*/
private $items;
When I fetch the object with that collection, I receive items as an array of two elements I've added in the first place. I serialize it
{
"id": 19,
"name": "collection dummy name",
"items": [
{
"id": 431,
"name": "Company 1"
},
{
"id": 435,
"name": "Company 2"
}
],
"createdAt": "2019-03-11T13:55:43+01:00",
"updatedAt": "2019-03-11T15:48:57+01:00"
},
Then I remove FIRST item:
$collection->removeItem($companyToRemove);
$em = $this->getDoctrine()->getManager();
$em->persist($collection);
$em->persist($companyToRemove);
$em->flush();
$results = $companiesCollectionRepository->getCollections($companyLoader->getCurrentCompany());
Serialize the $results and get not an array of one element, but object with key of second element from the previous array of items:
{
"id": 19,
"name": "collection dummy name",
"items": {
"1": {
"id": 435,
"name": "Company 2"
}
},
"createdAt": "2019-03-11T13:55:43+01:00",
"updatedAt": "2019-03-11T15:52:48+01:00"
},
When I reload the page and fetch this object, the collection is again one element array, not an object. Apparently Doctrine doesn't get new results from database, but returns data which has already loaded in the memory.
And serializer most likely treats this "array" as an "object", because it doesn't start with 0 for a key of first array element, but with key 1.
Is there any way to make that query again, so I get freshly generated keys, or refresh these keys?
EDIT:
Actually I've finally found simple solution for this: refresh after flush
$collection->removeItem($companyToRemove);
$em = $this->getDoctrine()->getManager();
$em->persist($collection);
$em->persist($companyToRemove);
$em->flush();
$em->refresh($collection);
I've faced the same problem. And here is another solution which seems working for me.
Custom normalizer, which will be used for collections with "holes" in they keys:
namespace App\Serializer\Normalizer;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerAwareTrait;
class DoctrineCollectionNormalizer implements NormalizerInterface, SerializerAwareInterface
{
use SerializerAwareTrait;
public function normalize($object, $format = null, array $context = array()): array
{
/* #var $object Collection */
$values = $object->getValues();
$object->clear();
foreach ($values as $k => $value) {
$object->set($k, $value);
}
return $this->serializer->normalize($object, $format, $context);
}
public function supportsNormalization($data, $format = null): bool
{
if ($data instanceof Collection) {
$keys = $data->getKeys();
$count = count($keys);
$lastKey = (int) array_pop($keys);
return $count && $lastKey !== $count-1;
}
return false;
}
}
I have a nested array from a json API that usually have zero, one, or two arrays.
I can get it so if the array is empty to display "N/A", and to display only the first ([0]) instance of the array.
However displaying the second array is tripping me up. I know I need to use the foreach to iterate through the PosTags (see below) array, so to extract each Tag value in the body of the foreach loop. However, I can't figure HOW to do this correctly.
My JSON:
[
{
"SectionDetails": [
{
"Description": "Course Description",
"Departments": "Department",
"Credits": "3.00",
"Meetings": [
{
"DOW": "Th",
"Dates": "08-31-2017 to 12-08-2017",
"Times": "03:00 PM - 05:30 PM",
}
],
"PosTags": [
{
"Tag": "HART-ANC"
},
{
"Tag": "ARCH-ARCH"
}
]
}
]
}
]
Current PHP:
$postag = "N/A";
if (isset($sectiondetails->{'PosTags'}))
{
if(!empty($sectiondetails->{'PosTags'})) {
$postag=$sectiondetails->{'PosTags'}[0]->{'Tag'};
}
}
Running a foreach loop like the following displays "Object of class stdClass could not be converted to string" on the line where echo tag is:
if (isset($sectiondetails->{'PosTags'})) {
if(!empty($sectiondetails->{'PosTags'})) {
$postag=$sectiondetails->{'PosTags'};
foreach ($postag as $tag) {
echo $tag;
}
}
}
Ideally I'd like HART-ANC, ARCH-ARCH to be displayed. Thoughts?
I am using PHP to connect with MongoDB. My code is as follows.
// connect
$m = new MongoClient($con_string); // connect to a remote host at a given port
$db = $m->main;
$customers = $db->customer->find();
i want to return $customers collection as json document to my HTML. How can i do this?
You can do this two ways:
echo json_encode(iterator_to_array($customers));
or you can manually scroll through it:
foreach($customers as $k => $row){
echo json_encode($row);
}
Each of MongoDBs objects should have their __toString() methods correctly implemented to bring back the representation of the value.
This also will work. And you can customize your json as well.
$arr = array();
foreach($customers as $c)
{
$temp = array("name" => $c["name"], "phone" => $c["phone"],
"address" => $c["address"]);
array_push($arr, $temp);
}
echo json_encode($arr);
Other answers work, but it is good to know that the generated JSON will have the following form (in this example I use an hypothetical "name" field for your customers):
{
"5587d2c3cd8348455b26feab": {
"_id": {
"$id": "5587d2c3cd8348455b26feab"
},
"name": "Robert"
},
"5587d2c3cd8348455b26feac": {
"_id": {
"$id": "5587d2c3cd8348455b26feac"
},
"name": "John"
}
}
So in case you don't want the Object _id to be the key of each of your result objects you can add a false parameter to iterator_to_array.
Your code would be:
echo json_encode(iterator_to_array($customers, false), true);
This creates the same result as
$result = Array();
foreach ($customers as $entry) {
array_push($result, $entry);
}
echo json_encode($result, true);
which is an array of JSON objects
[
{
"_id": {
"$id": "5587d2c3cd8348455b26feab"
},
"name": "Robert"
},
{
"_id": {
"$id": "5587d2c3cd8348455b26feac"
},
"name": "John"
}
]
Hi guys I was wonder how you can return the index based on ID with the following.
$friends = '{
"data": [
{
"name": "Paul",
"id": "12000"
},
{
"name": "Bonnie",
"id": "120310"
},
{
"name": "Melissa",
"id": "120944"
},
{
"name": "Simon",
"id": "125930"
},
{
"name": "Anthony",
"id": "120605"
},
{
"name": "David",
"id": "120733"
}
]
}';
$obj = json_decode($friends);
for example, I want to get print the name based on ID. I've had a go at using array_search but it didn't like the structure of the array. I will be using it inside a sql loop and passing the ID from the query to return the name string from the array.
print $obj->{'data'}[0]->{'name'};
//where 0 is returned index based on a defined ID.
thanks a lot
The json_decode makes your variables into objects. To do a search, you could use:
// The extra true at the end makes it decode to associative arrays
$json = json_decode($friends, true);
findByName($json, 1234);
function findNameById($obj, $id) {
foreach($obj as $o) {
if($o['id'] == $id) return $o['name'];
}
return false;
}
You will need PHP 5.3 in order to utilize the following closure and enhanced ternary operator (prints name if record is found; otherwise, prints 'No record found.'):
$findById = function($id) use ($friends) {
foreach (json_decode($friends)->data as $friend) {
if ($friend->id === $id) return $friend->name;
}
return;
};
echo $findById('125930') ?: 'No record found.';
Nice thing about this is that you can pass this callable around and it will remember the scope it was defined with. This means that you can pass any ID but the $friends array will always be available to the closure.