I'm using an API from a contributer for my hotel booking engine. Whenever i need some information from his system, i execute CURL and i get the response in XML. Then i apply json_decode(json_encode(simplexml_load_string($response)), true)
and i get the response in array. So far so good, but there is a specific problem. The returned XML for the same function doesn't have always the same format. It depends on the parameters of the request. I 'll use a specific example to make this clear.
When i need to get the availability of my room/rooms, i send a request with a range of days. There are 4 different situations here.
1 room - 1 day
1 room - multiple days
Multiple rooms - 1 day
Multiple rooms - Multiple days
In situation (1) the array is like this:
In situation (2) the array is like this:
In situation (3) the array is like this:
In situation (4) the array is like this:
As you can see, if there is 1 room, there is no 0 key as an index and the information seems like "1 level up". Same situation for days. If you request for more than 1 day, then all days have their index (0, 1, 2 etc..).
The question is: Is there a way to convert every response to the array like situation 4?
I mean if there is 1 room, place it's information under RoomAvailability['0']. If there are more, place them like RoomAvailability['1'], RoomAvailability['2'] etc...
Same thing for the DayAvailability. If there is one one day place the day's information under DayAvailability['0']. Many days? DayAvailability['1'], DayAvailability['2'] etc...
With this trouble the only thing i could do to check if there are many rooms was:
if (isset($response['RoomsAvailability']['RoomAvailability']['0'])) {
//Do staff
}
But that's pretty bad! I don't want to waste time and conditions if there is 1 room or 50, 1 day or 30!
Any1 can help?
Just do a pre-check if they're array, and if not, encapsulate it in an array.
First do that for the main array (in this case the second RoomsAvailability)
an array
if(array_key_exists('#attributes', ($response['RoomsAvailability']['RoomAvailability']))
{
$response['RoomsAvailability']['RoomAvailability'] = [$response['RoomsAvailability']['RoomAvailability']];
}
After that you need to run each room and do the same for the days
foreach($response['RoomsAvailability']['RoomAvailability'] as &$item)
{
if(array_key_exists('DayAvailability', $item))
{
$item = [$item];
}
}
Full code:
// check if the base room availability is a single room, if it is convert into an array
if(array_key_exists('#attributes', ($response['RoomsAvailability']['RoomAvailability']))
{
$response['RoomsAvailability']['RoomAvailability'] = [$response['RoomsAvailability']['RoomAvailability']];
}
// now do the same for all the days in rooms
foreach($response['RoomsAvailability']['RoomAvailability'] as &$item)
{
if(array_key_exists('DayAvailability', $item))
{
$item = [$item];
}
}
// now do you stuff
NOTICE: on the foreachI've added an & at &$item so that the changes in the foreach do have effect outside of it
I answer my own question because i found out what my problem is.
In this line json_decode(json_encode(simplexml_load_string($response)), true) if a node has children, the array is like $array[0][something], $array[1][something] etc... but if there is only one child the array goes like $array[something] without the 0 key! That was totally unexpected!
After a lot of search, it seems like there is no abstract and proper way to convert any XML to a php array, keep each node's attributes and keep the same structure as the XML was. Please correct me if i'm wrong.
I decided to use SimpleXMLElement php.net link, pretty simple and easy to use.
Related
I'm struggling with my lack of operational knowledge with handling or arrays and variables within a private function that is within a class in an applciation I've "inherited" control of.
The function loops for X days, within that loop, a certain number of MySQL rows will be returned, from a range of different queries. Each row from each query issues a +1 to a counter.
Some maths is then performed on the value of the counter (not really relevant)
Once X days have been iterated through, I need to discard all days calcualted coutner value EXCEPT the value that was the highest. I had planned on using max($array); for this.
I will greatly strip down the code for the purpose of this example:
class Calendar {
public $heights;
private fucntion dayLoop($cellNumber) {
$heights = []; //array
$block_count = 0; //counter
while(mysqlrowdata) {
[code for mysql operations]
$block_count++; //increment the count
}
$day_height = ($block_count * 16) + 18; //do some math specific to my application
$this->heights[] = $day_height; //commit calc'd value to array
//array_push($heights, $day_height); //this was a previosu attempt, i dont think i should use array_push here..??
}
}
This function is called on other "front end" pages, and if I perform a var_dump($heights); at the bottom of one of those pages, the array returns empty with
Array ( )
Essentially, at that front end page, I need to be able to A) inspect the array with vlaues from each looped day from X days, and B) pull the largest calc'd counter value from any of the X days that were iterated through in the loop.
I have tried a myriad of things, like changing the function to be public, defining the start of the array on the front end pages instead of anywhere in the class or fucntion. I declared array name as a variabel in the class as I read that I needed to do that.
Overall, I just don't really understand how I'm MEANT to be handling this, or if I'm going about it in completely the wrong way? Solutions very welcome, but advice and words of wisdom also appreciated greatly. Thanks.
In my database I have a table named posts. In it, the columns are:
Postid - 1
PostBody - my first post
Likers - 1,2,3,4,5,
Now what am trying to do is, in the likers field, am trying to pull the individual number without pulling all the data on the field. So if I want to retrieve likers No.3, I want the output to be 3 when I echo it out.
I have tried FIND_IN_SET(), and strstr(). All I get is the whole array, 1-5, being echoed. And because of that, I cannot match it with other variables(I.e. $var = 3;) which have the matching number.
Is there a way of doing this?
Thanx
If you really want to do it this way (read my comment first), what you can do is retrieve the comma-seperated data, explode it into a php array, and then use the array_search built-in function:
$likers_attribute_data = '1,2,3,4,5,';
$likers_array = explode(',', $likers_attribute_data);
// check if user 3 is a liker:
if (array_search('3', $likers_array) {
// he has :)
} else {
// he hasn't :(
}
Again, this is a very sloppy and inefficient way of doing business. Please just go with the solution in my comment!
I've made a script that pretty much loads a huge array of objects from a mysql database, and then loads a huge (but smaller) list of objects from the same mysql database.
I want to iterate over each list to check for irregular behaviour, using PHP. BUT everytime I run the script it takes forever to execute (so far I haven't seen it complete). Is there any optimizations I can make so it doesn't take this long to execute...? There's roughly 64150 entries in the first list, and about 1748 entries in the second list.
This is what the code generally looks like in pseudo code.
// an array of size 64000 containing objects in the form of {"id": 1, "unique_id": "kqiweyu21a)_"}
$items_list = [];
// an array of size 5000 containing objects in the form of {"inventory: "a long string that might have the unique_id", "name": "SomeName", id": 1};
$user_list = [];
Up until this point the results are instant... But when I do this it takes forever to execute, seems like it never ends...
foreach($items_list as $item)
{
foreach($user_list as $user)
{
if(strpos($user["inventory"], $item["unique_id"]) !== false)
{
echo("Found a version of the item");
}
}
}
Note that the echo should rarely happen.... The issue isn't with MySQL as the $items_list and $user_list array populate almost instantly.. It only starts to take forever when I try to iterate over the lists...
With 130M iterations, adding a break will help somehow despite it rarely happens...
foreach($items_list as $item)
{
foreach($user_list as $user)
{
if(strpos($user["inventory"], $item["unique_id"])){
echo("Found a version of the item");
break;
}
}
}
alternate solutions 1 with PHP 5.6: You could also use PTHREADS and split your big array in chunks to pool them into threads... with break, this will certainly improve it.
alternate solutions 2: use PHP7, the performances improvements regarding arrays manipulations and loop is BIG.
Also try to sort you arrays before the loop. depends on what you are looking at but very oftenly, sorting arrays before will limit a much as possible the loop time if the condition is found.
Your example is almost impossible to reproduce. You need to provide an example that can be replicated ie the two loops as given if only accessing an array will complete extremely quickly ie 1 - 2 seconds. This means that either the string your searching is kilobytes or larger (not provided in question) or something else is happening ie a database access or something like that while the loops are running.
You can let SQL do the searching for you. Since you don't share the columns you need I'll only pull the ones I see.
SELECT i.unique_id, u.inventory
FROM items i, users u
WHERE LOCATE(i.unique_id, u inventory)
I know my title is quite ambivalent but if you bear me, you'll find it is not so bad :P
I have a class method that gets quite different database fields in any order and builds an array based on those fields (each key adds another level into the array).
Now, that's the easy part. I also have a correction function that unifies the e.g. costs by some demographic data. The problem is that I need to address the right level in the correction formula.
I try to make an example:
I ask fields A,year,B,C,D and my correction formula for D depends on the year and C. I have formalized so that C and D are always the last ones to list but the problem is that how do I address the year so that I could get out an answer like [A][year][B]=function(year,c,d). The fields are in an array ($retr['fields']=array("A","year","B","C") (the result D comes automatically)
I tried to use foreach like
$retr['fields']=array("A","year","B","C")
$temp=get_data($retr);
foreach($temp as $${$retr['fields'][0]} => $yd)
foreach ($yd as $${$retr['fields'][1]} => $cd) {
$output[$${$retr['fields'][0]}][$${$retr['fields'][1]}]=0;
foreach ($cd as $a => $v)
$output[$${$retr['fields'][0]}][$${$retr['fields'][1]}]+=$v*$act[$year][$a]
and so on, but it seems that one can't use a variable variable as a key in foreach (or then I have got the syntax wrong). As now it just says that "Undefined variable: year"
Do you have any ideas how to express what I need?
Well I solved it, or rather not solved but programmed around.
For each foreach I checked the corresponding field and if it was year, I made a variable with value of current foreach key and referenced it with $$variable.
Like this:
$temp=get_data($retr);
foreach($temp as $varA => $yd) {
if ($retr[0]=="year")
$year_var=varA;
.
.
.
$output[$varA][$varB]+=$v*$act[$$year_var][$a]
I still would have preferred a cleaner solution but given my timeframe (or rather the loss of it) this should suffice.
I'm still interested in the "perfect solution"(TM)
Thx anyway :)
It's kind of hard to understand your structure, but it appears you're fetching some data items and then categorize them into tree-like structure.
In this case, even though a particular attribute (e.g. year) appears on some upper level it's still, essentially, an attribute of the data item itself, so keep it as such: convert the "value" into a non-scalar (either object or hash). Then you would be able to work with those attributes without referencing outer loop values (and without resorting to variable variables):
foreach($arr as $subarr) {
foreach ($subarr as $subsubarr) {
// ... and so on
foreach ($lastlevel as $value) {
$adjusted = correctionFunc($value->data, $value->year, $value->somethingelse); // assuming value is an object
}
}
}
I need to store some data that is essentially just an array of key-value pairs of date/ints, where the dates will always be unique.
I'd like to be able to store it like an associative array:
array(
"2012-02-26" => 5,
"2012-02-27" => 2,
"2012-02-28" => 17,
"2012-02-29" => 4
)
but I also need to be able to query the dates (ie. get everything where date > 2012-02-27), and so suspect that I'll need to use a schema more like:
array(
array("date"=>"2012-02-26", "value"=>5),
array("date"=>"2012-02-27", "value"=>2),
array("date"=>"2012-02-28", "value"=>17),
array("date"=>"2012-02-29", "value"=>4),
)
Obviously the former is much cleaner and more concise, but will I be able to query it in the way that I am wanting, and if not are there any other schemas that may be more suitable?
You've described two methods, let me break them down.
Method #1 - Associative Array
The key tool for querying by "associative array" is the $exists operator. Here are details on the operator.
So you can definitely run a query like the following:
db.coll.find( { $exists: { 'field.2012-02-27' } } );
Based on your description you are looking for range queries which does not match up well with the $exists operator. The "associative array" version is also difficult to index.
Method #2 - Array of objects
This definitely has better querying functionality:
db.coll.find( { 'field.date': { $gt: '2012-02-27' } } );
It can also be indexed
db.coll.ensureIndex( { 'field.date': 1 } );
However, there is a trade-off on updating. If you want to increment the value for a specific date you have to use this unwieldy $ positional operator. This works for an array of objects, but it fails for anything with further nesting.
Other issues
One issue with either of these methods is the long-term growth of data. As you expand the object size it will take more space on disk and in memory. If you have an object with two years worth of data that entire array of 700 items will need to be in memory for you to update data for today. This may not be an issue for your specific data, but it should be considered.
In the same vein, MongoDB queries always return the top-level object. Again, if you have an array of 700 items, you will get all of them for each document that matches. There are ways to filter out the fields that are returned, but they don't work for "arrays of objects".