How to find json direct parent based on children value - php

I need to find the direct parent of all instance of "type": "featured-product" in a JSON file using PHP. store this parent string in a variable. use a foreach.
In the example below, the variable would have the value "1561093167965" and "3465786822452"
I'm a little lost, thank you for the help!
{
"current": {
"sections": {
"1561093167965": {
"type": "featured-product"
},
"3465786822452": {
"type": "featured-product"
}
}
}
}
foreach ($json['current']['sections'] as $sectionName => $section) {
if ($section['type'] && $section['type'] == 'featured-product') {
$featuredId = $sectionName;
}
}

Another approach you can take is to create a new array containing only featured-products using array_filter and then extract the keys. From the docs:
If the callback function returns TRUE, the current value from array is
returned into the result array. Array keys are preserved.
$product_sections = array_keys(
array_filter($json['current']['sections'], function($val) {
return $val['type'] === 'featured-product';
}));
Demo
The problem in your original code is that your $featuredId variable is getting overwritten in each iteration of the loop, so when it ends its value will be the one of last element processed. If you have to deal with multiple values, you'll have to add it to an array or do the work directly inside the foreach. You can see the other answers for how to fix your code.

There is probably a cleaner way but this works using json_decode and iterating over the array with foreach
$json='{
"current": {
"sections": {
"1561093167965": {
"type": "featured-product"
},
"3465786822452": {
"type": "featured-product"
}
}
}
}';
$e=json_decode($json,true);
foreach($e['current']['sections'] as $id=>$a){
if($a['type']=='featured-product'){
echo 'the parent id is '.$id;
}
}

//change this with the real json
$json='{
"current": {
"sections": {
"1561093167965": {
"type": "featured-product"
},
"3465786822452": {
"type": "featured-product"
}
}
}
}';
$result = [];
$jsond=json_decode($json,true);
foreach($jsond['current']['sections'] as $k=>$v){
if($v['type']=='featured-product'){
$result[] = $k;
}
}

Related

Remove the item from array based on child value on object

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

Array merge on basis of same keys but different keys value should add as separate array

I have data like I have pasted bellow. I want to combine on the basis of name i.e abc name template is in different language i want it in the information name, category and All its languages on the same index.
[{
"name": "abc",
"category": "new_cat",
"selectedLanguage": [{
"de": "Deutsch",
"de_status": "APPROVED"
}]
}, {
"name": "abc",
"category": "new_cat",
"selectedLanguage": [{
"en": "English",
"en_status": "APPROVED"
}]
}, ....
As mentioned above I want the result as json pasted below.
[{
"name": "abc",
"category": "new_cat",
"selectedLanguage": [{
"de": "Deutsch",
"de_status": "APPROVED"
}, {
"en": "English",
"en_status": "APPROVED"
}]
},...{
"name": "unique_temp",
"category": "TICKET_UPDATE",
"selectedLanguage": [{
"en": "English",
"en_status": "REJECTED"
}, {
"fr": "French",
"fr_status": "REJECTED"
}]
}]
I have written a code for it as
$trimArr; //this data array
$finalTempArr = array();
$finalArr = array();
foreach ($trimArr as $tr) {
$checkIfExist = $this ->in_array_r($wr['name'], $finalArr);
if($checkIfExist == false){
$finalTempArr['name'] = $tr['name'];
$finalTempArr['category'] = $tr['category'];
$finalTempArr['selectedLanguage'] = $tr['selectedLanguage'];
}else {
array_push($finalTempArr['selectedLanguage'],$tr['selectedLanguage']);
}
array_push($finalArr, $finalTempArr);
}
echo json_encode($finalArr);
function in_array_r($needle, $haystack, $strict = false) {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) || (is_array($item)
&& $this->in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
You can just use array_reduce to accomplish it:
$result = array_reduce($array, static function(array $carry, array $item) {
$key = $item['name'] . $item['category'];
if (isset($carry[$key])) {
$carry[$key]['selectedLanguage'] = array_merge($carry[$key]['selectedLanguage'], $item['selectedLanguage']);
} else {
$carry[$key] = $item;
}
return $carry;
}, []);
if you need fresh keys after reducing, then you can use array_values on the result:
$result = array_values($result);
A simple foreach loop will do.
I want to combine on the basis of name
So just use the name as the temporary key while you group and merge the data.
Code: (Demo)
$result = [];
foreach (json_decode($json, true) as $row) {
if (!isset($result[$row['name']])) {
$result[$row['name']] = $row;
} else {
$result[$row['name']]['selectedLanguage'] = array_merge($result[$row['name']]['selectedLanguage'], $row['selectedLanguage']);
}
}
var_export(array_values($result));
The more I think about, your subarray structure is probably not fit for purpose. If you are eventually going to be searching for available languages to return the language status, it will be better to set up the array structure to be a "lookup". In other words, each selectedLanguage subarray should be a flat associative array using the language as the key and the language_status as the value. This would look like "en" => "APPROVED".
Alternative, if your scripts are already relying on the en key to find the appropriate values, then keep en as the key and make a flat associative subarray from en. I can't really say with 100% confidence what you should do, but I am reasonably sure that your data structure could be improved. Once you ditch the indexed subarray structure, you then may be able to implement a recursive merge/replace technique.

Implementing filter of nested arrays in PHP/Wordpress

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;
}, []);
}

Loop json object as array in PHP

How can i loop the response to get all ids and names?
If the response like this:
{
"Data": [
{
"Id": "4321",
"Name": "Dog"
},
{
"Id": "749869",
"Name": "Cat"
}
]
}
I can get all the ids and names using this code:
foreach (json_decode($response->content)->Data as $resp) {
echo $resp->Id;
echo $resp->Name;
}
But, if the response like this:
{
"Data": {
"dog": {
"Id": "4321",
"Name": "Dog"
},
"cat": {
"Id": "749869",
"Name": "Cat"
}
}
}
How can i get all the ids and names without knowing the amount of the array? Thank you.
You can get PHP to read everything into an (associative where applicable) array.
foreach (json_decode($response->content, true)["Data"] as $resp) { //2nd parameter is to decode as array
echo $resp["Id"];
echo $resp["Name"];
}
Example run: https://eval.in/954621
Alternatively you can do the lazy thing and just cast:
foreach ((array)json_decode($response->content)->Data as $resp) {
echo $resp->Id;
echo $resp->Name;
}
Well if you want to extract all ids into one array (I assume you mean all into one array), to use in a database query as an example. I would recommend you doing like this.
$ids = array_values(array_map(function($animal){
return intval($animal['Id']);
}, json_decode($response->content, true)['Data']));
var_dump($ids);
you do the same ! foreach loop would work on both.
the json response supposed to have the same format every time, unless you have a really weird API.
<?php
$response = json_decode($json);
foreach($response as $animal => $data)
{
echo $animal;
echo $data->id;
echo $data->name;
}

returning index from JSON returned array

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.

Categories