Unify variable types of array elements - php

After hours of debugging, I found an error in one of my scripts. For saving different event types in a database, I have an array of unique data for each event that can be used to identify the event.
So I basically have some code like
$key = md5(json_encode($data));
to generate a unique key for each event.
Now, in some cases, a value in the $data array is an integer, sometimes a string (depending on where it comes from - database or URL). That causes the outputs of json_encode() to be different from each other, though - once including quotes, once not.
Does anybody know a way to "unify" the variable types in the $data array? That would probably mean converting all strings that only contain an integer value to integer. Anything else I have to take care of when using json_encode()?

array_walk_recursive combined with a function you have written to the effect of maybe_intval which performs the conversion you talk about on a single element.
EDIT: having read the documentation for array_walk_recursive more closely you'll actually want to write your own recursive function
function to_json($obj){
if(is_object($obj))
$obj=(array)$obj;
if(is_array($obj))
return array_map('to_json',$obj);
return "$obj"; // or return is_int($obj)?intval($obj):$obj;
}

Related

Why is my php array being saved to MongoDB as an object, then being retrieved as an associative array with stringified keys?

Background:
I have a class in this application I'm building whose job is:
__construct: $this->data = $this->mongoDB->collection->findOne();
Intermediate functions are employed to manipulate the data in tens of different ways each request. One manipulation could trigger one which would trigger another. This allows me to do unlimited updates to the mongo document with just one query, as long as $this->data['_id'] remains the same. This is the only place where data manipulation of this specific collection is allowed.
__destruct: $this->monboDB->collection->save($data)
Data is then read back, json_encode'd and sent to Javascript to draw the page
Intention:
I intended to delete a member of an array by looping through said array, matching a value within it, and unsetting that. Example:
foreach($this->data['documents'] as $key => $val){
if($val == $toBeDeleted){
unset($this->data['documents'][$key];
}
}
Then, this would be saved to the DB when the script finishes.
Problem:
When javascript reads back the data, rather than having ['a', 'b', 'd'], I had {'0': 'a', '1': 'b', '3': 'd'} - which can't be treated like an array and would pretty much break things.
I had this question half typed out before my a-hah! moment, so I figured I'd post my own answer to it too for future reference.
In php, an associative array and an array are all the same. You can have out of order keys, nonconsecutive keys, and almost any key that you'd like to use in calling your array member. Most, if not all, php array functions work with any array key. Objects are a totally different thing.
That being said, Javascript doesn't share the same rules for arrays. A javascript array must have consecutive keys starting at zero, otherwise it is an object. MongoDB is similar to Javascript in this way.
When php converts an object to be used in MongoDB or in Javascript, if the php array doesn't follow that rule, it becomes a Javascript object.
The problem was after unsetting an array index, it left a gap, causing nonconsecutive array keys, causing it to become an object. Simple fix would either be array_slice($array, $key, 1) or $array = array_values($array)

Finding the length of an associative array encoded by PHP

I have a multidiminsional array that I have created in php that is passed back to a jQuery script. I need to iterate through this array and process the data.
In Firebug I can see that the data is located at data.data.items. I've tried finding the length of the array using data.data.items.length, but it comes back as undefined. Interestingly, this worked prior to my php portion working correctly when it passed back an array of 8 empty items. Now that it's populated (and the indexes are strings), length doesn't work. There is also an object in each of the items. What's breaking this?
An Array in JavaScript is an object nonetheless. When setting values using strings (or anything that isn't an integer), you are actually setting a property of the object (you are actually doing this when setting it with integer keys as well, but it's handled slightly differently).
To the issue of its sudden breakage after using strings as keys, I would expect that PHP realizes when you have an honest-to-goodness array versus an associative array, thus it sends arrays (surrounded by []) when all keys are integers, and objects (surrounded by {}) otherwise. I believe in the string-keyed case, PHP is generating objects, and thus .length becomes undefined (rather than 0 as in an empty array).
To answer your question, there is a simple way to count the "length" of this data:
var i = 0;
for (var item in data.data.items) {
i++;
}
Which will iterate through each property of data.data.items and count them. Note that if you (or any library you include) adds a property to the Object prototype, this will not produce expected results. This is fairly uncommon, but you must be aware of it when using for..in.
You can address this by using the method Nagh suggested, which ignores properties not defined on that particular object:
var i = 0;
for (var item in data.data.items) {
if(data.data.items.hasOwnProperty(item)) {
i++;
}
}
You can always use "foreach" kind of loop. In this case, you don't need to know what array length is there or even is it array or not, since you can iterate over object properties aswell.
As Joe already has pointed, javascript doesn't have associative arrays and when you trying to use one - you end up with object with properties. However, if u sure, that only properties this object got - is your array you can use code like that:
for (i in arr) {
if (arr.hasOwnProperty(i)) {
//do something with arr[i]
}
}
However if you really need an array, consider using integer as an array index.

Why would one want to pass primitive-type parameters by reference in PHP?

One thing that's always bugged me (and everyone else, ever) about PHP is its inconsistency in function naming and parameters. Another more recent annoyance is its tendency to ask for function parameters by reference rather than by value.
I did a quick browse through the PHP manual, and found the function sort() as an example. If I was implementing that function I'd take an array by value, sort it into a new array, and return the new value. In PHP, sort() returns a boolean, and modifies the existing array.
How I'd like to call sort():
$array = array('c','a','b');
$sorted_array = sort($array);
How PHP wants me to call sort():
$array = array('c','a','b');
sort($array);
$sorted_array = $array;
And additionally, the following throws a fatal error: Fatal error: Only variables can be passed by reference
sort(array('c','a','b');
I'd imagine that part of this could be a legacy of PHP's old days, but there must have been a reason things were done this way. I can see the value in passing an object by reference ID like PHP 5+ does (which I guess is sort of in between pass by reference and pass by value), but not in the case of strings, arrays, integers and such.
I'm not an expert in the field of Computer Science, so as you can probably gather I'm trying to grasp some of these concepts still, and I'm curious as to whether there's a reason things are set up this way, or whether it's just a leftover.
The main reason is that PHP was developed by C programmers, and this is very much a C-programming paradigm. In C, it makes sense to pass a pointer to a data structure you want changed. In PHP, not so much (Among other things, because references are not the same as a pointer).
I believe this is done for speed-reason.
Most of the time you need the array you are working on to be sorted, not a copy.
If sort should have returned a new copy of the array then for each time you call sort(); the PHP engine should have copied the array into new one (lowering speed and increasing space cost) and you would have no way to control this behaviour.
If you need the original array to be not sorted (and this doesn't happen so often) then just do:
$copy = $yourArray;
sort($yourArray);

Will the Order of my Associative Array be maintained from PHP to Javascript?

In PHP I'm running a mysql_query that has an ORDER BY clause. I'm then iterating through the results to build an associative array, with the row_id as the key.
Then, I'm calling json_encode on that array and outputting the result.
This page is loaded with AJAX, and defined in a Javascript variable. When I iterate through that Javascript variable, will I still have the order that was returned from the mysql_query?
PHP arrays are somewhat unique in their property of maintaining insertion order. Javascript doesn't have associative arrays per se. It has objects, which are often used as associative arrays. These do not guarantee any particular key order.
Why not output them as an array? That will have a particular order. If you want some sort of key lookup why does the order matter?
What cletus says is correct, but in my experience, most browsers will maintain the order. That being said, you should consider using an Array. If you need to sort it once you receive it on the client-side, just use the .sort() function in JavaScript:
rows.sort(function(a, b) {
return a.row_id - b.row_id;
}
Though it seems like it works, the order of properties in an object can't be counted on. See the many comments below for more info (smarter eyes than mine). However, this was the code I used to test the behavior in my own limited testing:
var test = {
one: 'blah',
two: 'foo',
another: 'bar'
};
for (prop in test) {
document.write(prop + "<br />");
}
Prints (in Firefox 3.6.3 and Chrome 5.0.375.9):
one
two
another
Also, you may want to be sure you're getting the type of JSON encoding you're needing back from json_encode(), such as an object (uses {} curly braces) and not an array ([] braces). You may need to pass JSON_FORCE_OBJECT to json_encode() to force it.
Edited to clarify that the Array approach is preferred)
Edited again (sorry), as I had overlooked pcorcoran's comment, which has a link to an issue in Chromium's issue tracker regarding this. Suffice to say, the order an object's properties is not reliable.

How can I convert all values of an array to floats in PHP?

I am fetching an array of floats from my database but the array I get has converted the values to strings.
How can I convert them into floats again without looping through the array?
Alternatively, how can I fetch the values from the database without converting them to strings?
EDIT:
I am using the Zend Framework and I am using PDO_mysql. The values are stored one per column and that is a requirement so I can't serialize them.
array_map('floatval', $array) only works on single dimensional arrays.
I can't floatval the single elements when I use them because I have to pass an array to my flash chart.
The momentary, non-generic solution is to extract the rows and do array_map('floatval',$array) with each row.
You could use
$floats = array_map('floatval', $nonFloats);
There is the option PDO::ATTR_STRINGIFY_FETCHES but from what I remember, MySQL always has it as true
Edit: see Bug 44341 which confirms MySQL doesn't support turning off stringify.
Edit: you can also map a custom function like this:
function toFloats($array)
{
return array_map('floatval', $array);
}
$data = array_map('toFloats', $my2DArray);
How are you getting your data? mysql, mysqli or PDO, some other way or even some other database?
you could use array_map with floatval like so:
$data = array_map('floatval', $data);
but that still executes a loop and i think it assumes you only have one column in your data.
you're probably best of casting to float when you use your value, if you have to. php is likely to do a good job of interpreting it right anyway.
LOL... are you working on the same project I am tharkun?
I just finished (last night) creating something, in a ZF based project, that uses pdo_mysql to retrieve and format data and then output it as xml for use in a flash piece. The values were going in as strings but needed to be floats.
Since I'm also the one who wrote the part that gets the data and the one who created the database I just made sure the data was converted to float before it went into the database.
I simply cast the values as float as part of some other formatting, for what it is worth.
protected function _c2f($input)
{
$input = (float)$input;
$output = round(($input * 1.8) + 32, 2);
return $output;
}
I found an easy way for this operation.
You can do this just by adding foreach loop to your code. foreach loop is used to fetch your array string data one by one. and then, you can simply convert this by function number_format. i used 2 place after convert to float value. i.e it used to print value after dot value 2 place.
$example= array("12.20", "15.05", "55.70");
foreach($example as $float)
{
$update_value = number_format($float,2);
echo $update_value."<br>";
}
Not sure what you're asking here? You can cast a string to a float, using (float) $string, but since PHP is dynamically typed, that will happen anyway, when needed. There is no reason to do an explicit cast.
What are you using floating point values for?

Categories