Remove JSON array index that holds specific value with PHP - php

I am brand new to php.I have found questions that show how to remove key/value pairs from JSON files with php, but not array indexes.
I have worked out how to append values to arrays in a JSON file with json_decode(). But not how to remove values. I need to produce a function() that hunts for c and removes any value within an array in my JSON file. Below is a before and after of the expected outcome I need to produce with my php file.
// before
[["a", "c", "b"], ["c", "c"], [], ["c", "d"], ["d"], ["e"]]
// after
[["a", "b"], [], [], ["d"], ["d"], ["e"]]
Below is the function I have produced in order to add values to arrays in my JSON if this helps provide more context:
function appendClient($file, $post, $client) {
$a = fopen($file, "r");
$json = json_decode(fread($a, filesize($file)));
$json[$post][] = $client;
fclose($a);
$a = fopen($file, "w");
fwrite($a, json_encode($json));
fclose($a);
}

Use array_filter
function removeClient($file, $post, $client) {
$json = json_decode(file_get_contents($file));
$json[$post] = array_filter($json[$post], function($x) use($client) {
return $x != $client;
});
file_put_contents($file, json_encode($json));
}
This assumes all the elements of the array are either empty arrays or 1-element arrays containing the client name, as in the example you showed.

Take a look at array_filter and array_values functions.
[["a"],[],["b"],["c"]]
From the above input, I am assuming you are working with 2d array. Then, you can use the following function to do the job:
function removeValues($array, $value) {
$result = [];
foreach ($array as $row) {
$filtered = array_filter($row, function($entry) use($value) {
return $entry != $value;
});
// If you need to reset keys
$filtered = array_values($filtered);
$result[] = $filtered;
}
return $result;
}
Example:
$input = [["a"],[],["b"],["c"]];
$output = removeValues($input, "c");
print_r($output);

Related

Reform PHP associated array

I have an associative array:
$input = [
['key'=>'x', 'value'=>'a'],
['key'=>'x', 'value'=>'b'],
['key'=>'x', 'value'=>'c'],
['key'=>'y', 'value'=>'d'],
['key'=>'y', 'value'=>'e'],
['key'=>'z', 'value'=>'f'],
['key'=>'m', 'value'=>'n'],
];
And I want to reform it simple in:
$output = [
'x'=>['a','b','c'],
'y'=>['d','e'],
'z'=>'f',
'm'=>'n'
]
So basically, conditions are:
1. If same key found then put values in an array.
2. If no same key found then value remains string.
You can replace associative array with object if you are more comfortable with objects.
Here is my working solution for this problem:
foreach($input as $in){
if(!empty($output[$in['key']])){
if(is_array($output[$in['key']])){
$output[$in['key']][] = $in['value'];
continue;
}
$output[$in['key']] = [$output[$in['key']],$in['value']];
continue;
}
$output[$in['key']] = $in['value'];
}
print_r($output);
However I believe that it can be done in much compact and efficient way.
Please comment your answers if someone has better solution.
Your help is much appreciated!
Reformat array to [ [ x=>a ], [x=>b],.. ] and merge all sub-arrays
$input = array_map(function($x) { return [$x['key'] => $x['value']]; }, $input);
$input = array_merge_recursive(...$input);
print_r($input);
demo
I would suggest
<?php
$input = [
['key'=>'x', 'value'=>'a'],
['key'=>'x', 'value'=>'b'],
['key'=>'x', 'value'=>'c'],
['key'=>'y', 'value'=>'d'],
['key'=>'y', 'value'=>'e'],
['key'=>'z', 'value'=>'f'],
['key'=>'m', 'value'=>'n'],
];
$reducer = function($carry, $item) {
$carry[$item['key']][] = $item['value'];
return $carry;
};
$mapper = function ($item) {
if (count($item) === 1) {
return $item[0];
}
return $item;
};
$output = array_map($mapper, array_reduce($input, $reducer, []));
var_dump($output);
You can see the result here: https://3v4l.org/8JjjS
You can use array_reduce to loop over an existing array and build up a new one:
$output = array_reduce($input, function ($carry, $i) {
$carry[$i['key']][] = $i['value'];
return $carry;
}, []);
Each element in $input is passed to the anonymous function, along with the $carry variable that's being built up as we go along. Inside, we just add each value to a sub-element indexed by key. The third argument [] is to set the initial value of the result to an empty array.
See https://eval.in/935015
(I'm assuming that the duplicate x keys in the question are a typo, and that the second is supposed to z, since that matches up with your suggested output)
For your original code you may find extract() interesting. I replaced the continue-s with else-s, but that is more like a matter of taste:
foreach($input as $in){
extract($in);
if(!empty($output[$key])){
if(is_array($output[$key])){
$output[$key][] = $value;
} else {
$output[$key] = [$output[$key],$value];
}
} else {
$output[$key] = $value;
}
On a side note I would probably use two much simpler loops, one for building lists and another for extracting single elements:
foreach($input as $in){
$output[$in['key']][] = $in['value'];
/* or: extract($in);
$output[$key][]=$value; */
}
foreach($output as $key => $value){
if(count($value)==1){
$output[$key]=$value[0];
}
}

How to get top 5 higher values in json files inside folder

public function onRun(int $currentTick){
foreach (glob($this->plugin->getDataFolder()."/players/*.json") as $plData) {
$str = file_get_contents($plData);
$json = json_decode($str, true);
$levels = $json["level"];
}
}
I want to get top 5 higher values from all json files in "players" folder, I only know how to get that value from all files, but don't know how to select 5 higher. Can someone help please?
EDIT: Json file looks like this:
{
"coins": 0,
"rank": "Guest",
"accept_friends": true,
"reward_time": 1440,
"level": "29",
"bio": "Today is very cool!c",
"progress": 24.939999999999998,
"local_ip": "10.0.0.1",
"registred": true,
"logged": true
}
Use usort!
Make a function that will handle your sorting:
function levelSort($a, $b)
{
return $a['level']>$b['level'];
}
next store your players to array, sort it and return first five elements:
public function onRun(int $currentTick){
$players = []; // declare aray with proper scope
foreach (glob($this->plugin->getDataFolder()."/players/*.json") as $plData) {
$str = file_get_contents($plData);
$json = json_decode($str, true);
$players[] = $json; // save to array
}
usort($players, 'levelSort'); // sort using custom function
return array_slice($players, 0, 5); // return 5 elements
}
Should work. didn't tested tho :D
Of course this example assuming that every $json is an array and $json['level'] exists and is int
You need to build an array of levels with $levels[] as you are overwriting $levels each time. Then just reverse sort and slice the top 5:
public function onRun(int $currentTick){
foreach (glob($this->plugin->getDataFolder()."/players/*.json") as $plData) {
$str = file_get_contents($plData);
$json = json_decode($str, true);
$levels[] = $json["level"];
}
rsort($levels);
return array_slice($levels, 0, 5);
}
If you want to return the entire top 5 arrays:
public function onRun(int $currentTick){
foreach (glob($this->plugin->getDataFolder()."/players/*.json") as $plData) {
$str = file_get_contents($plData);
$results[] = json_decode($str, true);
}
array_multisort(array_column($results, 'level'), SORT_DESC, $results);
return array_slice($results, 0, 5);
}
Why are you passing in an argument $currentTick and not using it? Maybe replace 5 with $currentTick so you can pass it in?

Using serialized data from AJAX object with PHP

I'm sending an entire form, about 8 fields, along with my AJAX data object, one of which is a serialized string:
var fields = $(this).serialize();
var data = {
action:'newSubmission',
nonce: Nonce,
fields: fields
};
Now within PHP I cannot understand how to get each field value from the $_POST object correctly.
$test = $_POST['field'];
Is the full string and sends back to JS a properly formatted JSON object. But how do I break up the serialized string correctly in PHP?
The string will be encoded, so you will have to do that with url_decode.
Here is a function that I used to build a workable object out of the string.
function urldecode_to_obj($str) {
$str = trim($str, '"');
// explode string before decoding
foreach (explode('&', $str) as $chunk) {
$param = explode("=", $chunk);
if ($param) {
// search string for array elements and look for key-name if exists
preg_match('#\[(.+?)\]$#', urldecode($param[0]), $with_key);
preg_match('#\[\]$#', urldecode($param[0]), $no_key);
$mkey = preg_split('/\[/', urldecode($param[0]));
// converts to array elements with numeric key
if ($no_key) {
$data[$mkey[0]][] = urldecode($param[1]);
}
// converts to array elements with named key
if ($with_key) {
$data[$mkey[0]][$with_key[1]] = urldecode($param[1]);
}
if (!$no_key && !$with_key) {
$data[urldecode($param[0])] = urldecode($param[1]);
}
}
}
return (object)$data;
}
$str = "serialized_string";
$obj = urldecode_to_obj($str);
Your variables with values are now in the object. If you have a var1 variable in there with a value1:
$obj -> var1 = "value1";
You can also get thins back as an array. Just omit the (object) casting and return the data in t\he function as:
return $data;
If you want to return things to your JS then you should echo them out as an array and use json_encode:
$array = array("success" => true);
echo json_encode($array);
Try using:
$serializedData = file_get_contents("php://input");
$unserializedData = array();
parse_str($unserializedData,$serializedData);
print_r($unserializedData);
instead of the PHP $_POST superglobal use the php://input.
PHP "php://input" vs $_POST
Then you can use theparse_str() php function to turn the serialized string into a php array.
There are also a number of other ways to manually do this if you so choose:
function unserializeForm($str) {
$returndata = array();
$strArray = explode("&", $str);
$i = 0;
foreach ($strArray as $item) {
$array = explode("=", $item);
$returndata[$array[0]] = $array[1];
}
return $returndata;
}

Convert CSV to JSON using PHP

I am trying to convert CSV file to JSON using PHP.
Here is my code
<?php
date_default_timezone_set('UTC');
$today = date("n_j"); // Today is 1/23/2015 -> $today = 1_23
$file_name = $today.'.CSV'; // My file name is 1_23.csv
$file_path = 'C:\\Users\\bheng\\Desktop\\qb\\'.$file_name;
$file_handle = fopen($file_path, "r");
$result = array();
if ($file_handle !== FALSE) {
$column_headers = fgetcsv($file_handle);
foreach($column_headers as $header) {
$result[$header] = array();
}
while (($data = fgetcsv($file_handle)) !== FALSE) {
$i = 0;
foreach($result as &$column) {
$column[] = $data[$i++];
}
}
fclose($file_handle);
}
// print_r($result); // I see all data(s) except the header
$json = json_encode($result);
echo $json;
?>
print_r($result); // I see all data(s)
Then I json_encode($result); and tried to display it, but nothing is displaying on the screen at all. All I see is the blank screen, and 0 error message.
Am I doing anything wrong ? Can someone help me ?
Added Result of print_r($result);
Array (
[Inventory] => Array (
[0] => bs-0468R(20ug)
[1] => bs-1338R(1ml)
[2] => bs-1557G(no bsa)
[3] => bs-3295R(no BSA)
[4] => bs-0730R-Cy5"
[5] => bs-3889R-PE-Cy7"
[6] => 11033R
[7] => 1554R-A647
[8] => 4667
[9] => ABIN731018
[10] => Anti-DBNL protein
.... more ....
Try like this:
$file="1_23.csv";
$csv= file_get_contents($file);
$array = array_map("str_getcsv", explode("\n", $csv));
$json = json_encode($array);
print_r($json);
data.csv
Game,Skill
Treasure Hunter,pilipala
Rocket Launcher,bibobibo
Rocket Engine,hehehohoho
To convert with column name, this is how I do it.
csv2json.php
<?php
if (($handle = fopen("data.csv", "r")) !== FALSE) {
$csvs = [];
while(! feof($handle)) {
$csvs[] = fgetcsv($handle);
}
$datas = [];
$column_names = [];
foreach ($csvs[0] as $single_csv) {
$column_names[] = $single_csv;
}
foreach ($csvs as $key => $csv) {
if ($key === 0) {
continue;
}
foreach ($column_names as $column_key => $column_name) {
$datas[$key-1][$column_name] = $csv[$column_key];
}
}
$json = json_encode($datas);
fclose($handle);
print_r($json);
}
The output result
[
{
"Game": "Treasure Hunter",
"Skill": "pilipala"
},
{
"Game": "Rocket Launcher",
"Skill": "bibobibo"
},
{
"Game": "Rocket Engine",
"Skill": "hehehohoho"
}
]
You can try this way too.
<?php
function csvtojson($file,$delimiter)
{
if (($handle = fopen($file, "r")) === false)
{
die("can't open the file.");
}
$csv_headers = fgetcsv($handle, 4000, $delimiter);
$csv_json = array();
while ($row = fgetcsv($handle, 4000, $delimiter))
{
$csv_json[] = array_combine($csv_headers, $row);
}
fclose($handle);
return json_encode($csv_json);
}
$jsonresult = csvtojson("./doc.csv", ",");
echo $jsonresult;
I ran into a similar problem, I ended up using this to recursively convert the data to UTF-8 on an array before encoding to JSON.
function utf8_converter($array)
{
array_walk_recursive($array, function(&$item, $key){
if(!mb_detect_encoding($item, 'utf-8', true)){
$item = utf8_encode($item);
}
});
return $array;
}
From:
http://nazcalabs.com/blog/convert-php-array-to-utf8-recursively/
This issue is pretty old by now, but hoping this helps someone, as it seemed like the simplest example I found, and I know this is a pretty common thing devs might need to do as a beginner, and lots of answers gloss over the magic.
$file = storage_path('app/public/waitlist_users_test.csv'); //--> laravel helper, but you can use any path here
function csv_to_json($file)
{
// file() loads each row as an array value, then array map uses the 'str_getcsv' callback to
$csv = array_map('str_getcsv', file($file));
// array_walk - "walks" through each item of the array and applies the call back function. the & in "&row" means that alterations to $row actually change the original $csv array, rather than treating it as immutable (*sort of immutable...)
array_walk($csv, function(&$row) use ($csv) {
// array_combine takes the header row ($csv[0]) and uses it as array keys for each column in the row
$row = array_combine($csv[0], $row);
});
array_shift($csv); # removes now very redundant column header --> contains {'col_1':'col_1', 'col_2':'col_2'...}
$json = json_encode($csv);
return $json;
}
There's a lot of magic going on with these functions that accept callback functions, that didn't seem to be explained thoroughly above. I'm self taught and have been programming for years, and find that it's often just glossed over without detailing how callbacks work, so I'll dive in just a little bit for the array_map('str_getcsv', file($file)) function - if you pass a function you've written, or inbuilt php function name as a string, it will take the value of whatever (in this case - array) element is being evaluated by the calling function (in this case array_map), and pass that to the callback function without the need to explicitly pass in a variable - super helpful once you get the hang of it, but I find it's not explained thoroughly very often which leaves beginners to not understand why it works, just that it works.
I've linked most of these above, but here's a little more information:
str-getcsv do? Array Walk Array Map Callables/Callbacks
as #MoonCactus noted, the file() function only loads 1 row at a time which helps save on memory usage for large .csv files.
Also, some other posts reference using explode - why not use explode() instead of str_getcsv() to parse rows? Because explode() would not treat possible enclosured parts of string or escaped characters correctly.
Hope somebody finds this helpful!
If you are converting a dynamic CSV file, you can pass the URL through a parameter (url=http://example.com/some.csv) and it will show you the most up-to-date version:
<?php
// Lets the browser and tools such as Postman know it's JSON
header( "Content-Type: application/json" );
// Get CSV source through the 'url' parameter
if ( isset( $_GET['url'] ) ) {
$csv = explode( "\n", file_get_contents( $_GET['url'] ) );
$index = str_getcsv( array_shift( $csv ) );
$json = array_map(
function ( $e ) use ( $index ) {
return array_combine( $index, str_getcsv( $e ) );
}, $csv
);
}
else {
$json = "Please set the path to your CSV by using the '?url=' query string.";
}
// Output JSON
echo json_encode( $json );
Alternate solution that uses similar method as #Whirlwind's solution but returns a more standard JSON result (with named fields for each object/record):
// takes a string of CSV data and returns a JSON representing an array of objects (one object per row)
function convert_csv_to_json($csv_data){
$flat_array = array_map("str_getcsv", explode("\n", $csv_data));
// take the first array item to use for the final object's property labels
$columns = $flat_array[0];
for ($i=1; $i<count($flat_array)-1; $i++){
foreach ($columns as $column_index => $column){
$obj[$i]->$column = $flat_array[$i][$column_index];
}
}
$json = json_encode($obj);
return $json; // or just return $obj if that's a more useful return value
}
The accepted answer uses file_get_contents() to read the entire file as a string in memory, and then explode() it to make it an array.
But it can be made faster, smaller in memory, and more useful:
function ReadCsv($fn)
{
$lines= file($fn); // read file directly as an array of lines
array_pop($lines); // you can remove the last empty line (if required)
$json= json_encode(array_map("str_getcsv", $lines), JSON_NUMERIC_CHECK);
print_r($json);
}
Nb: I used JSON_NUMERIC_CHECK here to avoid numbers being double quoted into strings. It also reduces the output size and it usually helps javascript on the other side (e.g. to compute or plot the data). Beware of phone numbers though!
I liked #ian-d-miller's solution for converting the data into a key / value style format, but I kept running into issues with his code.
Here's what worked for me:
function convert_CSV_to_JSON($csv_data){
// convert csv data to an array
$data = array_map("str_getcsv", explode("\n", $csv_data));
// use the first row as column headers
$columns = $data[0];
// create array to hold our converted data
$json = [];
// iterate through each row in the data
foreach ($data as $row_index => $row_data) {
// skip the first row, since it's the headers
if($row_index === 0) continue;
// make sure we establish each new row as an array
$json[$row_index] = [];
// iterate through each column in the row
foreach ($row_data as $column_index => $column_value) {
// get the key for each entry
$label = $columns[$column_index];
// add this column's value to this row's index / column's key
$json[$row_index][$label] = $column_value;
}
}
// bam
return $json;
}
Usage:
// as is
$json = convert_CSV_to_JSON($csv);
// encoded
$json = json_encode($json);
Something that i've made for myself and may be useful for others :)
This will convert CSV into JSON array with objects (key => value pair).
function csv2json($a, $e = true) {
$b = ["\r\n","\r","\n",];
foreach ($b as $c => $d) {
$a = explode($d, $a);
$a = isset($b[$c + 1]) ? implode($b[$c + 1], $a) : implode(PHP_EOL, $a);
}
// Convert to CSV
$a = array_map("str_getcsv", explode(PHP_EOL, $a));
// Get the first part of the array as the keys
$a = [
"keys" => array_shift($a),
"rows" => $a,
"row" => null,
];
// Define JSON
$b = [];
foreach ($a["rows"] as $a["row"]) {
$a["row"] = [ "csv" => $a["row"], "json" => (object)[], ];
for ($c = 0; $c < count($a["row"]["csv"]); $c++) {
$a["row"]["csv"][$c] = [#json_decode($a["row"]["csv"][$c]),$a["row"]["csv"][$c]];
// Switch from string to booleans, numbers and others
$a["row"]["csv"][$c] = isset($a["row"]["csv"][$c][0]) ? $a["row"]["csv"][$c][0] : $a["row"]["csv"][$c][1];
// Push it back
$a["row"]["json"]->{$a["keys"][$c]} = $a["row"]["csv"][$c];
}
$a["row"] = $a["row"]["json"];
$b[] = $a["row"];
unset($a["row"]);
}
// $e will be "return"
$e = $e ? json_encode($b) : $b;
// Unset useless variables
unset($a, $b, $c, $d);
return $e;
}
How to use?
If you want to return the JSON as a string, Leave it as default.
If you want to return the JSON as an object / array, set the second parameter to false.
Examples:
$csv = "name,age,gender
John Doe,35,male
Jane Doe,32,female";
echo csv2json($csv, true); // Or without the second parameter, just csv2json($csv)
The example above (^) will return a JSON stringified, Like this:
[{"name":"John Doe","age":35,"gender":"male"},{"name":"Jane Doe","age":32,"gender":"female"}]
and the example below:
var_dump(csv2json($csv, false));
will return a JSON array with these objects:
array(2) {
[0]=>
object(stdClass)#1 (3) {
["name"]=>
string(8) "John Doe"
["age"]=>
int(35)
["gender"]=>
string(4) "male"
}
[1]=>
object(stdClass)#2 (3) {
["name"]=>
string(8) "Jane Doe"
["age"]=>
int(32)
["gender"]=>
string(6) "female"
}
}
public function CsvToJson($fileContent){
//Convert CSV To Json and Return
$all_rows = array();
$newhead =array();
//Extract csv data to array on \n
$array = explode("\n",$fileContent);
//Extract csv header to array on 0 Index
$header = explode(",",$array[0]);
//Remove Header Row From Main Data Array
array_shift($array);
//Extract All Arrays To Saperate Orders
foreach($array as $arr){
$sliced = explode(",",$arr);
array_push($all_rows,$sliced);
}
//Extract All Orders Element To Saperate Array Item
foreach($all_rows as $row){
$sliced = explode(",",$arr);
array_push($all_rows,$sliced);
}
//Remove \r From Header Elements
foreach($header as $key=>$value){
$sliced = str_replace ("\r", "", $value);
array_push($newhead,$sliced);
}
//COMBINE Header as KEY And Row Element As Value
$arrrr = array();
foreach($all_rows as $row) {
//Remove Last Element of ROW if it is \r (Break given in css file for next row)
$count= count($row);
if ($row[$count-1] == "\r") {
array_splice($row, count($row) - 1, 1);
}
//CHECK IF HADER COUNT == ROW COUNT
if (count($header) == count($row)) {
array_push($arrrr,array_combine($newhead,$row));
}
}
//CONVERT ARRAY TO JSON
$json = json_encode($arrrr);
//Remove backslasesh from json key and and value to remove \r
$clean = stripslashes($json);
//CONVERT ARRAY TO JSON AGAIN FOR EXPORT
$jsonagain = json_encode($clean);
return $jsonagain;
}

How to set variables in an array PHP

I am trying to read in a text file which has 21 names like this;
123, bill, bobs
124, joe, public
I have been able to put them into an array but I don't know how to set each name as a variable, as I need to sort them into alphabetical order based on their last name. This is my code so far;
$file = fopen("students.txt", "r");
If ($file) {
while (!feof($file)) {
$array = explode("/n", fread($file, filesize("students.txt")));
print_r($array);
}
} else {
echo 'File unopened';
}
I have tried the following code but it doesnt seem to work;
fscanf ($fp, "%s, %s, %s/n", $num, $first, $last). "<br/>";
Just read the entire file into an array, and explode each line, then usort the results.
$array = file('your_file.txt');
if(false === $array) {
// you had a problem reading file
exit();
}
$exploded_array = array();
foreach($array as $csv) {
$exploded_array[] = explode(',', $csv)
}
usort($exploded_array, function ($a, $b) {
if ($a[2] == $b[2]) {
return 0;
}
return ($a[2] < $b[2]) ? -1 : 1;
}
You can also look at using fgetscv() instead of the combination of file() and explode(). Either way it is pretty much the same.
Your file related code is overcomplicated.
$users = file('students.txt', FILE_SKIP_EMPTY_LINES);
array_walk($users, function (&$val) {
$newAr = explode(',', $val);
$val = array_map('trim', $newAr);
});
usort($users, function($a, $b) {
return strnatcmp($a[2], $b[2]);
});
var_dump($users);
Example with fake data #3v4l
Alternativly you may use fgetcsv to get the values from the file as #MarkB pointed out in the comments of your question

Categories