All my AJAX requests are in json format which are being parsed in javascript.
How can i prevent null values being displayed in an HTML page without needing to write an if-statement in javascript for each json value?
Or should i write a custom PHP function to encode an array to json instead of using json_encode() so it doesn't display 'null' ?
In server side with PHP,
You can use array_filter before json_encode.
array_filter without second argument removes null elements of entry array, example :
$object= array(
0 => 'foo',
1 => false,
2 => -1,
3 => null,
4 => ''
);
$object = (object) array_filter((array) $object);
$result = json_encode($object);
The $result contains:
{"0":"foo","2":-1}
As you see, the null elements are removed.
I'm going to add to the accepted answer because that will only work if you have a 1 dimensional object or array. If there is any array or object nesting then in order to get the accepted solution to work, you must create some sort of recursive array filter. Not ideal.
The best solution my colleague and I came up with was to actually perform a regular expression on the JSON string before it was returned from the server.
$json = json_encode($complexObject);
echo preg_replace('/,\s*"[^"]+":null|"[^"]+":null,?/', '', $json);
The regular expression will remove all places in the string of the form ,"key":null including any whitespace between the leading comma and the start of the key. It will also match "key":null, afterwards to make sure that no null values were found at the beginning of a JSON object.
This isn't an ideal solution but it's far better than creating a recursive array filter given an object could be several dimensions deep. A better solution would be if json_encode had a flag that let you specify if you wanted null entries to remain in the output string.
To remove only NULL, but keep FALSE, '', and 0:
function is_not_null($var)
{
return !is_null($var);
}
echo json_encode(array_filter((array) $object, 'is_not_null'));
public function __toString() {
$obj = clone $this;
$keys = get_object_vars($obj);
foreach ($keys as $key => $value) {
if (!$value) {
unset($obj->{$key});
}
}
return json_encode($obj);
}
What about using the native JSON.stringify method on the javascript side?
You can set, a second parameter, a function to remove keys with a null value.
If you return undefined, the property is not included in the output JSON string (check the documentation for "the replacer parameter" at https://developer.mozilla.org/en-US/docs/Using_native_JSON#The_replacer_parameter).
function removeNulls(obj) {
var str = JSON.stringify(obj, function(key, val) {
if (val == null) return undefined;
return val;
});
return JSON.parse(str);
}
Then you can have a "normalized" JSON object by calling:
var obj = removeNulls(obj);
echo json_encode(array_filter((array) $object, function($val) {
return !empty($val);
}));
class Foo implements JsonSerializable
{
public $param1;
public $param2;
public function jsonSerialize()
{
return array_filter((array) $this, function ($var) {
return !is_null($var);
});
}
}
public function testJsonSerialization()
{
$expectedJson = '{"param1":true}';
$foo = new Foo();
$foo->param1 = true;
$foo->param2 = null;
$this->assertEquals(
$expectedJson,
json_encode($foo)
);
}`
of course you can create Abstract class with custom jsonSerialize method and extend all your DTOs from this
A version that works with multi-dimensional arrays.
$withoutNull = function($a) use (&$withoutNull) {
return array_filter(
array_map(
fn($p) => is_array($p) ? $withoutNull($p) : $p, $a
)
);
};
Example:
$a = [
"name" => "nathan",
"data" => [
"age" => 27,
"something" => null
],
"another" => null
];
$b = $withoutNull($a);
print_r($b);
Output:
Array
(
[name] => nathan
[data] => Array
(
[age] => 27
)
)
Related
I'm having trouble removing an object inside of this array.
First I get the $id that is to be removed from the array.
But when I'm filtering throw the array its appending keys to it.
So the logic ll no longer work on the rest of the application.
How can I maintain the same syntax on the options object after removing the object inside of the cart array ?
public function destroy( $id, Request $request )
{
$user = $this->user ;
$data = array_filter( $user->options->cart , function ( $option ) use ( $id ) {
if ( $option->product_id == $id ) {
return false;
}
return json_encode($option);
});
//dd($user->options->cart);
//dd($data);
$user->options = (object)['cart' => $data ];
$user->save() ;
return response()->json( $user , 200 ) ;
}
Solved :
public function destroy( $id, Request $request )
{
$user = $this->user ;
$data = array_filter( $user->options->cart , function ( $option ) use ( $id ) {
if ( $option->product_id == $id ) {
return false;
}
return true;
});
$user->options = ['cart' => array_values( $data ) ];
$user->save() ;
return response()->json( $user , 200 ) ;
}
}
if i understood u right , u want to rearrange the array after u do your logic plus keeping the structure , i would suggest you to use array_values
$new_data= array_values($data);
and if u got an error that its not an array although i doubt that just use the toArray() method
$new_data= array_values($data->toArray());
It seems like (object)['cart' => $data ] is somehow changing your array.
Setting the property directly should work:
$user->options->cart = $data;
Also, return json_encode($option); doesn't have any real effect except making the execution slower. You can just return true;.
Looking at your JSON encoding, I see that your options object on the bottom left is an object that contains a property, cart, which is an array. Your options object on the bottom right is an object that contains a property, cart, which is an object that contains a property for each numeric index.
I'm not at all certain, but I think the problem might be that the array_filter function preserves the array keys:
If the callback function returns TRUE, the current value from array is returned into the result array. Array keys are preserved.
I suggest you try using some approach that does not try to preserve array keys so that your filtered array has contiguous, numeric values.
public function destroy( $id, Request $request )
{
foreach($this->user->options->cart as $key => $cart_item) {
if ($cart_item->product_id == $id) {
unset($this->user->options->cart[$key]);
}
}
$user->save() ;
return response()->json( $user , 200 ) ;
}
EDIT: I am not privy to the details of your implementation (I don't know what type of object $user is or what $user->save or $response->json() might do) but this code will remove an array element by product_id:
$arr = array(
(object)["product_id" => 819, "name" => "I am 819"],
(object)["product_id" => 820, "name" => "I am 820"],
(object)["product_id" => 821, "name" => "I am 821"],
(object)["product_id" => 822, "name" => "I am 822"]
);
foreach($arr as $key => $v) {
if ($v->product_id == 820) {
unset($arr[$key]);
}
}
var_dump($arr);
From very long time i am working on php.
But one question may I have no idea about
like I have one function as bellow:
function hello($param1, $param2="2", $param3="3", $param4="4")
Now whenever I will use this function and if I need 4th params thats the $param4 then still I need to call all as blank like this one:
hello(1, '', '', "param4");
So is there any another way to just pass 1st and 4th param in call rather then long list of blanks ?
Or is there any other standard way for this ?
There was an RFC for this named skipparams but it was declined.
PHP has no syntactic sugar such as hello(1, , , "param4"); nor hello(1, default, default, "param4"); (per the RFC) for skipping optional parameters when calling a function.
If this is your own function then you can choose the common jQuery style of passing options into plug-ins like this:
function hello( $param1, $more_params = [] )
{
static $default_params = [
'param2' => '2',
'param3' => '3',
'param4' => '4'
];
$more_params = array_merge( $default_params, $more_params );
}
Now you can:
hello( 1, [ 'param4'=>'not 4, muahaha!' ] );
If your function requires some advanced stuff such as type hinting then instead of array_merge() you will need to manually loop $more_params and enforce the types.
One potential way you can do this, while a little bit hacky, may work well in some situations.
Instead of passing multiple variables, pass a single array variable, and inside the function check if the specific keys exist.
function hello($param1, $variables = ["param2" => "2", "param3" => "3", "param4" => "4"]) {
if(!array_key_exists("param2", $variables)) $variables['param2'] = "2";
if(!array_key_exists("param3", $variables)) $variables['param3'] = "3";
if(!array_key_exists("param4", $variables)) $variables['param4'] = "4";
echo "<pre>".print_r($variables, true)."</pre>";
}
This will allow you to set "param4" in the above variable, while still remaining default on all of the others.
Calling the function this way:
hello("test", ["param4" => "filling in variable 4"]);
Will result in the output being:
Array
(
[param4] => filling in variable 4
[param2] => 2
[param3] => 3
)
I don't generally recommend this if it can be avoided, but if you absolutely need this functionality, this may work for you.
The key here is that you have a specifically named index inside the array being passed, that you can check against inside the function itself.
The answer, as I see it, is yes and no.
No, because there's no way to do this in a standard fashion.
Yes, because you can hack around it. This is hacky, but it works ;)
Example:
function some_call($parm1, $parm2='', $parm3='', $parm4='') { ... }
and the sauce:
function some_call_4($parm1, $parm4) {
return some_call($parm1, '', '', $parm4);
}
So if you make that call ALOT and are tired of typing it out, you can just hack around it.
Sorry, that's all I've got for you.
It is an overhead, but you can use ReflectionFunction to create a class, instance of which that can be invoked with named parameters:
final class FunctionWithNamedParams
{
private $func;
public function __construct($func)
{
$this->func = $func;
}
public function __invoke($params = [])
{
return ($this->func)(...$this->resolveParams($params));
}
private function resolveParams($params)
{
$rf = new ReflectionFunction($this->func);
return array_reduce(
$rf->getParameters(),
function ($carry, $param) use ($params) {
if (isset($params[$param->getName()])) {
$carry[] = $params[$param->getName()];
} else if ($param->isDefaultValueAvailable()) {
$carry[] = $param->getDefaultValue();
} else {
throw new BadFunctionCallException;
}
return $carry;
},
[]
);
}
}
Then you can use it like this:
function hello($param1, $param2 = "2", $param3 = "3", $param4 = "4")
{
var_dump($param1, $param2, $param3, $param4);
}
$func = new FunctionWithNamedParams('hello');
$func(['param1' => '1', 'param4' => 'foo']);
Here is the demo.
Is there a way to turn an associative array into parameters for a function.
I have a simply array such:
$arr = [
'id' => 321654,
'name' => 'action1'
];
I have a static function in a class:
class someclass{
static function someFunction( $id, $name ){
//the rest of the method
}
}
I can call the class by variables, eg:
$class = 'someclass';
$method = 'somFunction';
return $class::$method( );
I can also pass is in a definite qty of function parameters, or an array
return $class::$method( $arr );
In this example's case I could hard code the params to:
return $class::$method( $arr['id'], $arr['name'] );
But how would i pass an unknown qty of keys. Another run may contain 1 key or 4 or 10...
Thanks to #Rizier123 comment:
This worked very nicely:
call_user_func_array( ''.$class.'::'.$method, $arr );
Im having a lot of difficulty with arrays in PHP. They require me to write a lot of codes such as isset(), empty(), array_key_exist(); And I really dont want to deal with these. If the key doesnt exist just handle it as a null.
$arr = [
'location' => 'Paris'
]
$arr['country'] // boom crash. How to walkaround this?
Any suggestions?
EDIT
I dont want to use any if condition. No isset(), array_key_exist, exceptions, etc. I just want them to be null if the key doesn't exist? Is this possible in PHP? The application is very abstract and data may vary on each request.
function getValue(array $array, $key) {
return isset($array[$key]) ? $array[$key] : null;
}
echo getValue($mysteryArray, 'mysteryKey');
Or:
$array += array_fill_keys(array('foo', 'bar', 'baz'), null);
echo $array['foo'];
My own function inspired from deceze. Works perfectly.
/**
* Fill array with null on nonexistent keys
*
* #param array $arg
* #param array $possible_keys
*/
function fillNull(array $arg, array $possible_keys){
foreach($possible_keys as $key){
$result[$key] = empty( $arg[$key] ) ? null : $arg[$key];
}
return $result;
}
You can use ArrayIterator or some class that gives you the interface you desire.
<?php
class MyArrayIterator extends ArrayIterator {
public function __construct($array, $flags=0) {
parent::__construct($array, $flags);
}
public function offsetGet($index) {
if (!$this->offsetExists($index)) {
return null;
}
return parent::offsetGet($index);
}
}
$arr = [
'location' => 'Paris'
];
$arrIt = new MyArrayIterator($arr);
echo $arrIt['country'];
echo "Only this is echoed";
#$arr['country'] - suppress errors, bad pratice.
&$arr['country'] - use reference, could add additional elements to array, bad pratice.
I have made this little class:
class Analyzer {
public static function analyze($phrases) {
$sortedPhrases = array();
array_walk($phrases, array('self', 'splitByLength'), $sortedPhrases);
var_dump($sortedPhrases);
}
private static function splitByLength($item, $key, &$resArr) {
// line stolen from here: http://stackoverflow.com/a/4786840/603003
// thanks to arnaud576875 <http://stackoverflow.com/users/576875/arnaud576875>
$len = count( preg_split('#\PL+#u', $item, -1, PREG_SPLIT_NO_EMPTY) );
if (!isset($resArr[$len])) {
$resArr[$len] = array();
}
$resArr[$len][] = $item;
var_dump($resArr);
}
}
$phrases = array(
"I can't believe the great content",
"I can't understand the superior information",
"I can't comprehend the amazing data",
"I cannot analyze the amazing data",
"I haven't written to the amazing data"
);
Analyzer::analyze($phrases);
Executing the script results in the output below:
array (size=1)
7 =>
array (size=1)
0 => string 'I can't believe the great content' (length=33)
...
array (size=3)
7 =>
array (size=3)
0 => string 'I can't believe the great content' (length=33)
1 => string 'I can't understand the superior information' (length=43)
2 => string 'I can't comprehend the amazing data' (length=35)
6 =>
array (size=1)
0 => string 'I cannot analyze the amazing data' (length=33)
8 =>
array (size=1)
0 => string 'I haven't written to the amazing data' (length=37)
array (size=0)
empty
All outputs are actually correct except the last one which comes from Analyzer::analyze(). It seems that the variable $sortedPhrases is somehow cleared after array_walk().
Take a better look at array_walk's documentation page.
userdata
If the optional userdata parameter is supplied, it will be passed as
the third parameter to the callback funcname.
That's the third parameter. It's not a reference, it's just a value that gets passed to your callback function.
One (of many) solutions to your problem is to use an object instead (objects are always passed by reference):
class Analyzer {
public static function analyze($phrases) {
$arrObj = new ArrayObject();
array_walk($phrases, array('self', 'splitByLength'), $arrObj);
var_dump($arrObj->getArrayCopy());
}
private static function splitByLength($item, $key, $arrObj) {
// line stolen from here: http://stackoverflow.com/a/4786840/603003
// thanks to arnaud576875 <http://stackoverflow.com/users/576875/arnaud576875>
$len = count( preg_split('#\PL+#u', $item, -1, PREG_SPLIT_NO_EMPTY) );
if (!isset($arrObj[$len])) {
$arrObj[$len] = array();
}
$arrObj[$len][] = $item;
var_dump($arrObj->getArrayCopy());
}
}
(it doesn't have to be an ArrayObject, it can be a stdClass object with an array property, or create your own class if you want...)
Or you can wrap your call within an anonymous function if you really want to work with a reference:
$static = get_called_class();
array_walk($phrases, function($item, $key) use($static, &$sortedPhrases){
$static::splitByLength($item, $key, $sortedPhrases);
});
While the third argument sent to the callback function cannot be a reference itself, it can contain references.
A different approach (to using objects or wrapping your callback in a closure) is to pass an array to the callback, containing a reference to the variable you wish to update. The wrapping array gets passed by-value, but all it contains is the referenced value that you want to change.
class Analyzer {
public static function analyze($phrases) {
// …
array_walk($phrases,
array('self', 'splitByLength'),
array(&$sortedPhrases));
// …
}
private static function splitByLength($item, $key, $extra_args) {
$resArr = &$extra_args[0];
// …
}
}
Pretty sure this is wrong:
array_walk($phrases, array('self', 'splitByLength'), $sortedPhrases);
So, Try this:
$sortedPhrases = array_walk($phrases, 'splitByLength');
And return your values in $sortedPhrases and don't use pass by reference (personal thing)