Good afternoon guys, I'm having a hard time finding the solution to this simple problem, I hope someone can help me.
I have a recursive array automatically generated by the source code, I use this array as the system's menu tree, however, it goes through a permission system, and some submenus are excluded, leaving certain menus empty, follow the example in JSON:
{
"1": {
"id": 1,
"idFather": null,
"nome": "test 1",
"sub": {
"4": {
"id": 4,
"idFather": 1,
"nome": "test 1.1",
"sub": {}
},
"5": {
"id": 5,
"idFather": 1,
"nome": "test 1.2",
"sub": {
"7": {
"id": 7,
"idFather": 5,
"nome": "test 1.3.1",
"sub": {}
},
"8": {
"id": 8,
"idFather": 5,
"nome": "test 1.3.2",
"sub": {}
}
}
},
"6": {
"id": 6,
"idFather": 1,
"nome": "test 1.3"
}
}
},
"2": {
"id": 2,
"idFather": null,
"nome": "test 2"
},
"3": {
"id": 3,
"idFather": null,
"nome": "test 3",
"sub": {
"10": {
"id": 10,
"idFather": 3,
"nome": "test 3.2"
}
}
}
}
Within key 1 I have key 4 with no other items, and I have key 5 with two keys (7 and 8), however, these 2 keys also do not contain items, so I need to remove keys 4, 7, 8 and consequently, key 5 too, as it will be empty at the end of the removals!
Note that key 6 inside key 1, key 10 inside key 3 and key 2 do not contain the "sub" element, so it must not be removed!
I am Brazilian, so my English may be a little rusty.
A simple recursive function will handle this.
Check each entry at the current level.
If it has a sub menu, call the function on the sub menu. If not, move on.
If the sub menu is now empty, remove it
The implementation looks like this:
$jsonString = "{...}"; // Your data listed above, omitted here for clarity
$menus = json_decode($jsonString);
// Pass a menu into the function.
// PHP passes objects by reference, so we're operating directly
// on the original object, hence no need for a return value.
function clearMenu(object $menu):void {
foreach($menu as $id=>$entry) {
if (isset($entry->sub)) { // If we have a submenu, handle it
clearMenu($entry->sub);
if (empty((array)$entry->sub)) { // Cast object to array to test for emptiness
unset($menu->$id); // Unset the item in the menu using the key. Unsetting the item directly doesn't work
}
}
}
}
clearMenu($menus);
$newMenus = json_encode($menus, JSON_PRETTY_PRINT);
echo "<pre>$newMenus</pre>";
Output:
{
"1": {
"id": 1,
"idFather": null,
"nome": "test 1",
"sub": {
"6": {
"id": 6,
"idFather": 1,
"nome": "test 1.3"
}
}
},
"2": {
"id": 2,
"idFather": null,
"nome": "test 2"
},
"3": {
"id": 3,
"idFather": null,
"nome": "test 3",
"sub": {
"10": {
"id": 10,
"idFather": 3,
"nome": "test 3.2"
}
}
}
}
Related
I've been stuck in this since yesterday any help will be much appreciated. The situation is this: I have a table for navigation links with the following structure,
id | parent_id | text
In my scenario the parent_id may be null(no sub-menu) or any id pointing back to the table which will make the record a sub-menu of the parent. I've tried many different ways to execute this but with no luck, the closest scenario I came up with is the following,
// All navigation link results are stored in $data
public static function transform(array &$data) {
$result = [];
$search = function(&$array = [], $parent = null) use (&$search, &$result){
foreach($array as $key => $value) {
/** If the element has an ancestor */
if($parent && $parent->id == $value->parent_id) {
$next = $parent->children[] = $value;
$search($array, $next);
unset($array[$key]);
}
}
return $parent;
};
foreach($data as $val) {
$result[] = $search($data, $val);
}
return $result;
}
The function works fine and tracks all the ancestors of a certain link but in the next iteration is tracking all the ancestors of the next element(even if it was a child of the previous one) so it comes back with a lot of duplicated data. I came up with the solution to unset the iterated element from the array but for some reason, the element is still being used in the next iteration.
Here is my current data
Here is my current result
A simple concept for faster search and to process one item only once.
Create a temp array with key as id and values as array of child ids. Will use this array to process next direct childs. It would be a depth first traversal.
Also set id as key in input array, so we can directly access whole node when required.
function generateTree($data){
$arrChild = []; // Store parent as key and its childs as array to quickly find out.
foreach($data as $obj){
$arrChild[$obj->parent_id][] = $obj->id;
$data[$obj->id] = $obj;
}
$final = [];
$setChild = function(&$array, $parents) use (&$setChild, $data, $arrChild){
foreach($parents as $parent){
$temp = $data[$parent];
// If key is set, that means given node has direct childs, so process them too.
if(isset($arrChild[$parent])){
$temp->children = [];
$setChild($temp->children, $arrChild[$parent]);
}
$array[] = $temp;
}
};
// Empty key would represent nodes with parent as `null`
$setChild($final, $arrChild['']);
return $final;
}
$final = generateTree($arr);
echo json_encode($final, JSON_PRETTY_PRINT);
Output:
[
{
"id": 1,
"navigation_id": 4,
"parent_id": null,
"text": "link 1",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 2,
"navigation_id": 4,
"parent_id": 1,
"text": "link 2",
"icon": "",
"url": null,
"page_id": 4,
"children": [
{
"id": 3,
"navigation_id": 4,
"parent_id": 2,
"text": "link 3",
"icon": "fas fa-ad",
"url": "https:\/\/google.com",
"page_id": null
},
{
"id": 4,
"navigation_id": 4,
"parent_id": 2,
"text": "link 4",
"icon": "fab fa-google",
"url": "https:\/\/google.com",
"page_id": null
}
]
}
]
},
{
"id": 5,
"navigation_id": 4,
"parent_id": null,
"text": "link 5",
"icon": "",
"url": null,
"page_id": 5,
"children": [
{
"id": 6,
"navigation_id": 4,
"parent_id": 5,
"text": "link 6",
"icon": "",
"url": null,
"page_id": 4
},
{
"id": 7,
"navigation_id": 4,
"parent_id": 5,
"text": "link 7",
"icon": "",
"url": null,
"page_id": 4
}
]
}
]
Well, I have a web project and I have to be saving things temporarily, I started work with a json file, so far I can add and update.
The json file looks like this:
[
{
"username": "Baldwin",
"products": [
{
"id": 0,
"amount": 10
},
{
"id": 1,
"amount": 9
},
{
"id": 2,
"amount": 9
}
]
},
{
"username": "Alice",
"products": [
{
"id": 0,
"amount": 11
},
{
"id": 1,
"amount": 13
},
{
"id": 2,
"amount": 6
}
]
},
{
"username": "Terry",
"products": [
{
"id": 0,
"amount": 12
},
{
"id": 1,
"amount": 14
},
{
"id": 2,
"amount": 5
}
]
}
]
The problem comes when I want to delete an specific array or when I want to delete it completely, I can do it and it works fine, but I have the doubt about why when I delete the object, other fields are add to the json file, like an id.
When i delete just one product inside of the "products" array something like this happen:
[
{
"username": "Baldwin",
"products": { "1": { "id": 1, "amount": 9 }, "2": { "id": 2, "amount": 9 } }
},
{
"username": "Alice",
"products": [
{ "id": 0, "amount": 11 },
{ "id": 1, "amount": 13 },
{ "id": 2, "amount": 6 }
]
},
{
"username": "Terry",
"products": [
{ "id": 0, "amount": 12 },
{ "id": 1, "amount": 14 },
{ "id": 2, "amount": 5 }
]
}
]
And when i delete a complete array from the json file, something like this happen:
{
"1": {
"username": "Alice",
"products": [
{ "id": 0, "amount": 11 },
{ "id": 1, "amount": 13 },
{ "id": 2, "amount": 6 }
]
},
"2": {
"username": "Terry",
"products": [
{ "id": 0, "amount": 12 },
{ "id": 1, "amount": 14 },
{ "id": 2, "amount": 5 }
]
}
}
My php file to delete:
<?php
// load file
$data = file_get_contents('results.json');
// decode json to associative array
$json_arr = json_decode($data, true);
$flag = false;
// We check if the user wants to delete all or just one product
if(isset($_POST["all"])):
$username = $_POST["username"];
foreach ($json_arr as $key => $value):
// find the username on the json file
if($value["username"] == $username):
unset($json_arr[$key]);
break;
endif;
endforeach;
elseif(isset($_POST["one"])):
$username = $_POST["username"];
$id = $_POST["id"];
foreach ($json_arr as $key => $value):
// find the username on the json file
if($value["username"] == $username):
// loop products of the current username
foreach ($json_arr[$key]["products"] as $k => $product):
// find the id of the product
if($json_arr[$key]["products"][$k]["id"] == (int)$id):
// delete the product
unset($json_arr[$key]["products"][$k]);
endif;
endforeach;
endif;
endforeach;
endif;
// encode json and save to file
file_put_contents('results.json', json_encode($json_arr));
// redirect to show.php
header("Location: show.php");
?>
I've been taking a look to questions like this one but i couldn't find something with php, i would like to know how to solve this or if this is normal.
What happens when you use unset($json_arr[0]) is that the first element is removed, but the keys are not updated. If you inspect the array after the removal, you'll find that your array has two elements, at $json_arr[1] and $json_arr[2].
When you then perform a json_encode($json_arr) on this, PHP's JSON decoder looks at the array and since arrays are supposed to begin at the 0th element but this array begins at 1, it decides that in order to preserve the keys, the array would have to be converted to an associative array - which transforms the integer array keys into string keys in JSON.
For a short and quick solution, you can try:
$json_arr = array_diff($json_arr, [$key]);
You could even use array_splice or array_values - see here for inspiration.
Given the following two arrays, how can they be merged efficiently to result in the third array?
productData
$productData =
[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]
products
$products =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}
productsFinal ($productData merged into $products - based on matching combo of product_id/product_id and type/product_type)
$productsFinal =
{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"product_type": "electronic",
"product_id": 6,
// How to merge product data and wrap with "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
},
{
"id": 6,
"type": "electronic",
"product_id": 5,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
}
},
{
"id": 9,
"type": "kitchen",
"product_id": 4,
// How to merge product data and wrap in "data" key
"data": {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
}
]
}
I tried different things for the outcome in a foreach loop but still cannot get it to render as intended:
foreach($productData as $productDataItem) {
// when $productDataItem.product_id == $product.product_id && $productDataItem.type == $product.product_type
// move the matching $productDataItem object into matching $product object, wrapped in a new "data" key
}
I don't know Laravel too well. However you can join your data objects quite easily:
<?php
$productData = json_decode('[
{
"product_id": 4,
"type": "electronic",
"name": "monitor",
"specs": {
"HDMI": true,
"VGA": false
}
},
{
"product_id": 5,
"type": "electronic",
"name": "HDMI cable",
"specs": {
"length": "3ft"
}
},
{
"product_id": 6,
"type": "kitchen",
"name": "spoon"
}
]');
$products = json_decode('{
"products": 3,
"per_page": 10,
"current_page": 1,
"data": [
{
"id": 4,
"type": "electronic",
"product_id": 6
},
{
"id": 6,
"type": "electronic",
"product_id": 5
},
{
"id": 9,
"type": "kitchen",
"product_id": 4
}
]
}');
// combine both data objects
foreach($products->data As &$p) {
foreach($productData As $d) {
if(property_exists($p, "product_id") && property_exists($d, "product_id") && property_exists($p, "type") && property_exists($d, "type")) {
if($p->product_id==$d->product_id && $p->type==$d->type) {
//$p = (object) array_merge((array) $p, (array) $d);
$p->data = $d; // updated answer
continue;
}
}
}
}
echo("<pre>");
echo json_encode($products, JSON_PRETTY_PRINT);
?>
You can test the code here: http://sandbox.onlinephpfunctions.com/code/98a50c35ee32c30f0d2be1661f7afb5895174cbe
Update: http://sandbox.onlinephpfunctions.com/code/aeebfdcf4f4db5e960260e931982570cfed19e0e
I would suggest to check this package dingo/api. I assume you want to display some kind of JSON response. Take a look at Transformers. You can do something like this :
<?php
namespace App\Http\Transformers;
use App\Http\Controllers\ProductData;
use League\Fractal\TransformerAbstract;
class ProductsDataTransformer extends TransformerAbstract
{
/**
* Turn this item object into a generic array
*
* #return array
*/
public function transform(ProductData $productdata)
{
return [
'id' => $productdata->id,
'product_type' => $productdata->product_type,
'product /*or data*/' => Product::find($productdata->product_id),
];
}
}
This would find the product by it's ID and look like this :
{
"id": 4,
"product_type": "electronic",
"product" {
"product_id": 6,
"type": "kitchen",
"name": "spoon"
},
},
You can then also create a transformer for Product to take care of your specs attribute to do the same thing.
Okay, So I am wanting to find information in an array and get a block returned based on the credentials passed. The way I am doing it right now is not working, I'm looking for a shorter process and a more fool proof process.
Right now I have this:
public function get_product($product_id, $color, $size)
{
$results = $this->pf->get('products/'.$product_id);
$vars = $results['variants'];
$details = array();
foreach($vars as $var)
{
if(!in_array($product_id, $details))
{
if($var['product_id'] == $product_id)
{
if($var['size'] == $size)
{
if($var['color'] == $color)
{
$details[$var['id']] = array(
'id' => $var['id'],
'name' => $var['name'],
'image' => $var['image'],
'price' => $var['price'],
);
}
}
}
}
}
return $details;
}
This receives a product_id, a color, and a size. Sometimes $color is null, Sometimes $size is null, and sometimes both $color and $size are null and we just need to find the one array that matches the $product_id.
What I am wanting returned is this:
$details[$var['id']] = array(
'id' => $var['id'],
'name' => $var['name'],
'image' => $var['image'],
'price' => $var['price'],
);
Right now nothing gets returned. $results returns this for an example: (This is what I need to search.)
{
"code": 200,
"result": {
"product": {
"id": 1,
"type": "POSTER",
"brand": null,
"model": "Poster",
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/poster_18x24.jpg",
"variant_count": 9,
"files": [
{
"id": "default",
"title": "Print file",
"additional_price": null
},
{
"id": "preview",
"title": "Mockup",
"additional_price": null
}
],
"options": []
},
"variants": [
{
"id": 4464,
"product_id": 1,
"name": "Poster 12×12",
"size": "12×12",
"color": null,
"color_code": null,
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/1/4464.jpg",
"price": "9.00"
},
Notice how color is returned as null. size can be that way to. So basically I am wanting a quicker and better way to search the returned array for the specified product_id, size, and color. So I need returned and matching the corresponding variants block that matches the variables submitted.
I hope I've made sense of what I'm trying to accomplish.
UPDATE
This is what I am needing.
So on my site the customers chooses a product, in this case a poster. Before adding it to the cart they are prompted to select a size. Let's say a 12x12. The way the API works is that it has a "top" item and then has smaller items "variants" that include the size and color. Each variant is a poster with a different size. The only way to obtain the poster product, is by receiving every variant for the poster. But each "variant" has a different "id" to send to the api to order the correct product.
So, I receive the product and it's variants in bulk or every color and size as it's own variant.
"variants": [
{
"id": 4464,
"product_id": 1,
"name": "Poster 12×12",
"size": "12×12",
"color": null,
"color_code": null,
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/1/4464.jpg",
"price": "9.00"
},
{
"id": 1349,
"product_id": 1,
"name": "Poster 12×16",
"size": "12×16",
"color": null,
"color_code": null,
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/1/1349.jpg",
"price": "11.00"
},
But remember the customer wanted a poster that was 12x12? We only need to send the demand to print a 12x12 poster. So we need to send to the api the ID for the variant that matches the 12x12 size.
I need a way to search through each variant for a product and find the correct variant that matches the product_id of the poster, and the size requirements of 12x12.
{
"id": 4464,
"product_id": 1,
"name": "Poster 12×12",
"size": "12×12",
"color": null,
"color_code": null,
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/1/4464.jpg",
"price": "9.00"
},
Once I find that correct variant, I need to collect all that information into a new array and return it.
//Get the product based on the supplied product_id. ($results)
//Break that array down into just the variants. ($vars)
//Search the $vars array for a block that matches the product_id.
//Search those $vars blocks for a single one that matches the size.
//If color is supplied, search those $vars blocks for a single one that matches the color.
//If size and color are supplied, a single block should be returned that matches all three variables (product_id, size, and color). Sometimes size and/or color is `null`. But a product_id is always supplied.
I hope the clears up what I am needing a little better.
Try this. Am also ssuming you are using php. If you have a question, asking me directly. I think I can help you but I don't know exactly what you want.
<?php
function get_product($object){
$result = json_decode($object);
$product_id = $result->result->product->id;
$variants = $result->result->variants;
$details = array();
foreach($variants as $variant):
$details[] = $variant;
endforeach;// foreach
return $details;
}
$json_obj = '{
"code": 200,
"result": {
"product": {
"id": 1,
"type": "POSTER",
"brand": null,
"model": "Poster",
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/poster_18x24.jpg",
"variant_count": 9,
"files": [
{
"id": "default",
"title": "Print file",
"additional_price": null
},
{
"id": "preview",
"title": "Mockup",
"additional_price": null
}
],
"options": []
},
"variants": [
{
"id": 4464,
"product_id": 1,
"name": "Poster 12×12",
"size": "12×12",
"color": null,
"color_code": null,
"image": "https://d1yg28hrivmbqm.cloudfront.net/products/1/4464.jpg",
"price": "9.00"
}
]
}
}';
$array = json_decode($json_obj);
echo '<pre>';
print_r(get_product($json_obj));
echo '</pre>';
I searched around for the answer to this but couldn't find anything and was looking for help from you wonderful people. I have been toying around with steam web API for my site. I have found this code and am using it. Also, I have barely any experience in steam api, most of my experience is in C. Anyway, here is the code:
[insert_php]
$api = "http://api.steampowered.com/IEconItems_440/GetPlayerItems/v0001/?key=MyApiKey&steamid=MySteamId&format=json";
$json = (file_get_contents($api));
$schema = json_decode($json);
print var_dump($schema);
[/insert_php]
I am using a plugin and inserting this php into my WordPress page. This code goes into my steam backpack and gives me all the items I have in a complicated list. What I need help with is condensing it so that it can be easily read. A defining feature of the inventory items is the defindexes. What I want to do is have it so if it finds a certain amount of one item with the same defindex, it will return that amount like this into my page: Scrap Metal = # of defindexes of Scrap Metal found. I hope that this is clear enough and that there is an answer. Thank you.
Part of my code that is returned now:
{
"id": 1828947688,
"original_id": 1176490973,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483650,
"quantity": 1,
"origin": 4
},
{
"id": 1828947700,
"original_id": 1176491289,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483651,
"quantity": 1,
"origin": 4
},
{
"id": 1828947742,
"original_id": 1178541917,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483652,
"quantity": 1,
"origin": 4
},
{
"id": 1828947755,
"original_id": 1178542060,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483757,
"quantity": 1,
"origin": 4
},
{
"id": 1828947766,
"original_id": 1179066746,
"defindex": 5005,
"level": 1,
"quality": 6,
"inventory": 2147483653,
"quantity": 1,
"origin": 4
},
{
"id": 1828947780,
"original_id": 1181421843,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483756,
"quantity": 1,
"origin": 4
},
{
"id": 1828947788,
"original_id": 1181426745,
"defindex": 5006,
"level": 1,
"quality": 6,
"inventory": 2147483654,
"quantity": 1,
"origin": 4
},
{
"id": 1828947793,
"original_id": 1187413384,
"defindex": 5007,
"level": 1,
"quality": 6,
"inventory": 2147483755,
"quantity": 1,
"origin": 4
},
{
"id": 1828947796,
"original_id": 1187413535,
"defindex": 5007,
"level": 1,
"quality": 6,
"inventory": 2147483655,
"quantity": 1,
"origin": 4
},
{
"id": 1828947801,
"original_id": 1187416362,
"defindex": 5007,
"level": 1,
"quality": 6,
"inventory": 2147483754,
"quantity": 1,
"origin": 4
},
{
"id": 1828947810,
"original_id": 1190342559,
"defindex": 5013,
"level": 1,
"quality": 6,
"inventory": 2147483656,
"quantity": 1,
"origin": 4
},
{
"id": 1828947826,
"original_id": 1190342965,
"defindex": 5013,
"level": 1,
"quality": 6,
"inventory": 2147483753,
"quantity": 1,
"origin": 4
},
{
"id": 1828947835,
"original_id": 1243518373,
"defindex": 5011,
"level": 1,
"quality": 6,
"inventory": 2147483657,
"quantity": 1,
"origin": 4
}
ETC.
You will need to loop through each item in your result $schema and search for the defindexes you are looking for.
For example, if you want to count all of your metal, you'll be looking for 5000, 5001, and 5002.
$metal_array = array(5000, 5001, 5002);
foreach ($schema as $item)
{
if (in_array($metal_array, $item->defindex)
{
// Do something for the $item. Presumably, you'll add these up
// So that you can get a total metal count (remember that refined is worth 9 scrap
// and reclaimed is worth 3)
}
}
Edit:
To answer some of the questions in your comment:
$item->defindex is referring to the defindex of the current $item object in your schema. You are performing object iteration in this foreach loop. Each item is being iterated over in the foreach loop. So, on the first pass through the loop, $item will look like this:
{
"id": 1828947688,
"original_id": 1176490973,
"defindex": 5009,
"level": 1,
"quality": 6,
"inventory": 2147483650,
"quantity": 1,
"origin": 4
}
You can access any of these values by doing $item->ATTRIBUTENAME (ie. $item->quality or $item->origin). On this pass through the loop, it will check if $item->defindex, which equals 5009 is in the $metal_array, which contains 5000, 5001, and5002`. It does not, so the if block does not execute.
As for what you'd do in that loop, I'd probably modify the code slightly to do something like this:
$metal_array = array(5000, 5001, 5002);
$total_metal = 0;
foreach ($schema as $item)
{
if (in_array($metal_array, $item->defindex)
{
switch ($item->defindex)
{
case 5000:
$total_metal++; // or $total_metal += 1;
break;
case 5001:
$total_metal += 3;
break;
case 5002:
$total_metal += 9;
break;
}
}
}
At the end of this block, you will have a value in $total_metal that equals the scrap value of all of your metal.
This block uses the switch statement to determine how much a particular item is worth. The break statement within each case prevents the logic from "falling through" to the next option.
For example, if the break was not added to the end of the case 5000 block of code and an $item->defindex equaled 5000, it would add 1 and then 3. This is not the answer you want. It is, however, a valid option to consider in certain circumstances.
switch ($item->defindex)
{
case 5000:
$total_metal++; // or $total_metal += 1;
case 5001:
$total_metal += 3;
break;
}
Finally, if you are new to PHP, I recommend taking a look at the documentation: http://php.net/manual/en/index.php