Delete object from json file with PHP - php

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.

Related

PHP - How to change json values according to another json

I am new to PHP programming and I need your lights!
I have a JSON file like:
"data": [{
"DIAGNOSTIC_TYPE_ID": 1,
"VALUE": "288.0"
},
{
"DIAGNOSTIC_TYPE_ID": 2,
"VALUE": "-0.1"
},
{
"DIAGNOSTIC_TYPE_ID": 3,
"VALUE": "327.67"
},
{
"DIAGNOSTIC_TYPE_ID": 1,
"VALUE": "288.0"
},
{
"DIAGNOSTIC_TYPE_ID": 2,
"VALUE": "-0.1"
},
{
"DIAGNOSTIC_TYPE_ID": 3,
"VALUE": "327.67"
}]
and I have to change it using another json file which is like:
"diagnostics_keys": [
{
"ID": 1,
"type": "BTTPV",
"key": "0_193_bttpv",
"unit": "V"
},
{
"ID": 2,
"type": "BTTPC",
"key": "0_195_bttpc",
"unit": "A"
},
{
"ID": 3,
"type": "AVGKMKWH",
"key": "0_202_avgkmKwh",
"unit": "Km/Kwh"
}]
How can I combine these two (using the ID and type keys/values of the second json and replace the DIAGNOSTIC_TYPE_ID with those on first json)and take a result like the bellow json?
"data": [{
"DIAGNOSTIC_TYPE_ID": BTTPV,
"VALUE": "288.0"
},
{
"DIAGNOSTIC_TYPE_ID": BTTPC,
"VALUE": "-0.1"
},
{
"DIAGNOSTIC_TYPE_ID": AVGKMKWH,
"VALUE": "327.67"
},
{
"DIAGNOSTIC_TYPE_ID": BTTPV,
"VALUE": "288.0"
},
{
"DIAGNOSTIC_TYPE_ID": BTTPC,
"VALUE": "-0.1"
},
{
"DIAGNOSTIC_TYPE_ID": AVGKMKWH,
"VALUE": "327.67"
}]
Would anyone have any points or links that may know and may help?
//change to arrays
$data = json_decode(YOUR_DATA_JSON, true); // I don't know your json variable names, so replace them
$diagnosticKeys = json_decode(YOUR_DIAGNOSTIC_KEYS_JSON, true);
//iterate over data, this is the one you want to change
foreach ($data as &$dataItem) {//(& is for replacing the values)
//another foreach for diagnostic keys
foreach ($diagnosticKeys as $diagnosticKey) {
if ($dataItem["DIAGNOSTIC_TYPE_ID"] == $diagnosticKey["ID"] {
$dataItem["DIAGNOSTIC_TYPE_ID"] = $diagnosticKey["type"];
}
}
}
//change to json again
$data = json_encode($data);
Not tested but should work.
$data1 = json_decode($json1);
$data2 = json_decode($json2);
$result = [];
foreach ($data1->data as $key => $value) {
foreach($data2->diagnostics_keys as $i => $val){
if($value->DIAGNOSTIC_TYPE_ID==$val->ID){
$value->DIAGNOSTIC_TYPE_ID = $val->type;
$result[$key]['DIAGNOSTIC_TYPE_ID'] = $val->type;
$result[$key]['VALUE'] = $value->VALUE;
}
}
}
echo json_encode($result);

How can I encode an array into json with additional data?

I am creating a json file from my array:
$file = json_encode($array);
The json file will look like this:
[
{
"name": "file1.html",
"date": "2019-01-29T20:33:57.000163Z",
"size": "348"
}
{
"name": "file2.xml",
"date": "2019-01-29T20:33:57.000167Z",
"size": "401"
}
{
"name": "file3.html",
"date": "2019-01-29T20:33:57.000171Z",
"size": "1314"
}
]
But I need to create a json file with some little bit different format. The output I need is:
{
"draw": 1,
"recordsTotal": 5000,
"recordsFiltered": 5000,
"data": [
{
"name": "file1.html",
"date": "2019-01-29T20:33:57.000163Z",
"size": "348"
}
{
"name": "file2.xml",
"date": "2019-01-29T20:33:57.000167Z",
"size": "401"
}
{
"name": "file3.html",
"date": "2019-01-29T20:33:57.000171Z",
"size": "1314"
}
]
}
Is this possible with json_encode?
Create a new array with rest of the info and assign current array data into it as well.
$newArray = array(
'draw'=> 1,
'recordsTotal'=> 5000,
'recordsFiltered'=> 5000,
'data'=>$array
);
$file = json_encode($newArray);

PHP - Most efficient way to filter two arrays

I have a question on what is the most performant way to filter two arrays of objects. I have two arrays of products from different systems and i want to work out which products have been removed from one array and then return the products that have been removed.
See the current function i have below which i know is super slow.
public function checkRemove($externalProducts, $localProducts){
//Push all the SKU codes from feed to an array();
$arr = [];
foreach ($externalProducts->products as $product) {
if($product->StockNumber != null){
array_push($arr, $product->StockNumber);
}
}
//Loop through the local products
$productsRemove = [];
foreach ($localProducts->products as $key => $localProduct) {
if(in_array($localProduct->sku, $arr)){
}else{
array_push($productsRemove, $localProduct);
}
}
return $productsRemove;
}
$externalProducts = {
"Filter": {
"Title": "All Products"
},
"Products": [{
"Type": "Jacket",
"Price": 75,
"ExpiryDate": "2018-06-30",
"StockNumber": "180220/003",
"Created": "2018-02-20 12:24:06",
"Modified": "2018-05-30 02:00:23"
},
{
"Type": "Jeans",
"Price": 150,
"ExpiryDate": "2018-06-30",
"StockNumber": "180221/004",
"Created": "2017-08-10 15:11:44",
"Modified": "2018-05-30 02:00:22"
},
{
"Type": "Jacket",
"Price": 240,
"ExpiryDate": "2018-06-30",
"StockNumber": "150804/012",
"Created": "2015-08-04 17:03:42",
"Modified": "2018-05-30 02:00:22"
}
]
}
$internalProducts = "localProducts": [{
"title": "Fur Coat",
"id": 16526,
"created_at": "2018-05-17T10:15:45Z",
"updated_at": "2018-05-17T10:15:45Z",
"sku": "180514/001",
"price": "75.00",
"regular_price": "75.00",
"categories": [
"Jackets",
],
},
{
"title": "Ripped Jeans",
"id": 16527,
"created_at": "2018-05-17T10:15:45Z",
"updated_at": "2018-05-17T10:15:45Z",
"sku": "180221/004",
"price": "150.00",
"regular_price": "150.00",
"categories": [
"Jeans",
],
},
{
"title": "Leather Jacket",
"id": 16528,
"created_at": "2018-05-17T10:15:45Z",
"updated_at": "2018-05-17T10:15:45Z",
"sku": "150804/012",
"price": "240.00",
"regular_price": "240.00",
"categories": [
"Jackets",
],
}
]
Take a look at array_filter
You can provide a callback function which will be run for each element in the array. If the callback function returns true, the current value from the array is returned in the result array.
You still have to iterate over one array at least. It is $localProducts. So, for $localProducts there're no improvements. But you can improve $externalProducts - add a special method (if you can) that will return StockNumbers only. More effective will be if StockNumbers will be of structure as:
[
'stocknumber1' => true,
'stocknumber2' => true,
'stocknumber3' => true,
'stocknumber4' => true,
'stocknumber5' => true,
]
This will improve your search, as checking isset($StockNumbers['stocknumber4']) is faster than in_array or array_search.
If you can't change structure of $externalProducts->products, than build array of stock numbers in a loop:
public function checkRemove($externalProducts, $localProducts){
//Push all the SKU codes from feed to an array();
$arr = [];
foreach ($externalProducts->products as $product) {
if ($product->StockNumber != null){
// Again I add sku as key, not as value
$arr[$product->StockNumber] = true;
}
}
//Loop through the local products
$productsRemove = [];
foreach ($localProducts->products as $localProduct) {
// check with `isset` is faster
if (isset($arr[$localProduct->sku])) {
array_push($productsRemove, $localProduct);
}
}
return $productsRemove;
}

Laravel 5.3 - Merge array based on 2 matching keys?

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.

How to not display information if it exists in one of two arrays

The situation is I have two arrays that is collecting JSON data via the API :
$players = getAPI("http://xx.xxx.xxx.xx:xxxxx/players.json?apiKey=xxxxxxxxxxxxxxxxxxxxxxxx");
$recents = getAPI("xx.xxx.xxx.xx:xxxxx/recent.json?apiKey=xxxxxxxxxxxxxxxxxxxxxxxx");
The method is getting the contents and decoding the JSON into an array.
For the players array we have this data in an array:
$players
[
{
"id": "76561198033377272",
"name": "PitMonk",
"position": {
"x": -339,
"y": 26,
"z": 191
},
"rotation": 128,
"time": 418310,
"ip": "",
"inventory": {
"main": [],
"belt": [
{
"name": "rock",
"amount": 1,
"blueprint": false,
"condition": 100
},
{
"name": "torch",
"amount": 1,
"blueprint": false,
"condition": 100
}
],
"wear": []
}
},
{
"id": "76561198088638439",
"name": "Pippa",
"position": {
"x": -337,
"y": 25,
"z": 177
},
"rotation": 73,
"time": 419136,
"ip": "",
"inventory": {
"main": [
{
"name": "arrow.wooden",
"amount": 12,
"blueprint": false
},
{
"name": "bow.hunting",
"amount": 1,
"blueprint": false,
"condition": 93
},
{
"name": "blueprint_fragment",
"amount": 25,
"blueprint": false
},
{
"name": "metal.fragments",
"amount": 1366,
"blueprint": false
},
{
"name": "metal.refined",
"amount": 48,
"blueprint": false
},
{
"name": "charcoal",
"amount": 1120,
"blueprint": false
},
{
"name": "lowgradefuel",
"amount": 738,
"blueprint": false
}
],
"belt": [
{
"name": "rock",
"amount": 1,
"blueprint": false,
"condition": 100
},
{
"name": "torch",
"amount": 1,
"blueprint": false,
"condition": 100
},
{
"name": "pickaxe",
"amount": 1,
"blueprint": false,
"condition": 76
},
{
"name": "pickaxe",
"amount": 1,
"blueprint": false,
"condition": 17
},
{
"name": "pickaxe",
"amount": 1,
"blueprint": false,
"condition": 100
},
{
"name": "pickaxe",
"amount": 1,
"blueprint": false,
"condition": 100
}
],
"wear": [
{
"name": "burlap.shirt",
"amount": 1,
"blueprint": false
},
{
"name": "attire.hide.skirt",
"amount": 1,
"blueprint": false
}
]
}
}
]
$recents
[
{
"id": "76561198039206786",
"name": "JakeGroves"
},
{
"id": "76561198088638439",
"name": "Pippa"
},
{
"id": "76561198033377272",
"name": "PitMonk"
},
{
"id": "76561198146864439",
"name": "YepWellDone"
},
{
"id": "76561198164836207",
"name": "Baz"
},
{
"id": "76561198076406281",
"name": "xwalnutx"
},
{
"id": "76561197985716090",
"name": "Darkflame134"
},
{
"id": "76561198263423842",
"name": "XitaikiznerX"
},
{
"id": "76561198129952244",
"name": "NatanGamer"
},
{
"id": "76561198071842055",
"name": "Baha Bey"
}
]
As you can see the players is the people connected, and recents is the total list of people who have connected recently.
I have attempted this:
foreach ($players as $player) {
echo $players->name;
}
echo "</br></br>";
foreach ($recent as $rec) {
if ($rec->name != $player->name) {
echo $rec->name . "</br>";
}
}
and it produces the result:
PitMonk Pippa
JakeGroves
PitMonk
YepWellDone
Baz
xwalnutx
Darkflame134
XitaikiznerX
NatanGamer
Baha Bey
So it is only ignoring 'pippa', I am not sure if it is possible to interact with two arrays as such for unique values?
You are interested in listing all names out of $users which don't exist in $players or $recents, right?
Assuming you are only interested in the name:
// First, let's get a new array with all names from both arrays (can contain dups)
$pcNames = array_map($players + $recents, function($playerObject) {
return $playerObject->name;
});
// Next, let's remove all dups
$pcNames = array_unique($pcNames);
// === At this point you have an array with all names from `$players` and `$recents` ===
// === You may do something else with those, but I'll now create another array with ===
// === all users not in the players/recents lists. ===
// Now let's also get a list of names of users in the `$users` variable
$userNames = array_map($users, function($playerObject) {
return $playerObject->name;
});
// And finally let's get all names which are not in players or recents
$diffNames = array_diff($userNames, $pcNames);
// Let's output those to see whether it worked
var_dump($diffNames);
Of course there are other ways depending on your use case. We could for example extract the names of all three arrays and then just use array_diff with 3 arguments (but then you don't have the $pcArray side product), or if you actually want to compare IDs but print names, we would have to change all the inline functions to extract ID instead of name and further down reference the users array to get the actual name, etc.

Categories