Splitting up an array formed by an api in PHP - php

I'm working on some stat tracking code for a game (yes, it's runescape. sue me).
I want to pull information from the high scores using an api which produces an array looking like this (captured using print_r)
Array
(
[getHiscore] => Array
(
[overall] => Array
(
[rank] => 61995
[lvl] => 2273
[totalxp] => 193310588
)
[attack] => Array
(
[rank] => 93406
[lvl] => 97
[totalxp] => 11747494
)
...and so on.
My question is how can i take what this api gives me and place it into a database table; I want to get an array such as this for a particular user and update their stats with it.
Would i use explode? It seems like the right idea to me but how would i actually use it to split the separate numbers and words?
My database is not totally realised yet, however each record will include a user name and then the level and total xp in each of 27 "skills". This almost certainly not best practice for database design but i'm as novice as they come so it's the best i can do.

You don't need to explode. It's an array and therefore it's already exploded. You need to iterate through it to build meaningful queries.
I'm not familiar with runescape so I don't know if your example is showing just the relevant fields of you need to discard some part. If it's the former, then you should try something like
$query="insert into stats_table (description, rank, lvl, totalexp) values (:description, :rank, :lvl, :totalext);";
$Statement = $DBConn->prepare($query);
foreach($fullArray as $description=>$subArray) {
$Statement->bindValue(':description',$description, PDO::PARAM_STR);
foreach($subArray as $key=>$value) {
$Statement->bindValue($key,$value, PDO::PARAM_STR);
}
$Statement->execute();
}
That will insert two rows containing
overall 61995 2273 193310588
attack 93406 97 11747494
PD: $DBConn is a PDO connection instance to whatever DB engine you're using.

Let's say this is all stored in the variable $ary:
$ary['getHiscore']['overall']['rank'] = 61995;
$ary['getHiscore']['overall']['lvl'] = 2273;
$ary['getHiscore']['overall']['totalxp'] = 193310588;
$ary['getHiscore']['attack']['rank'] = 93406;
$ary['getHiscore']['attack']['lvl'] = 97;
$ary['getHiscore']['attack']['totalxp'] = 11747494;
So, to get those values, it's:
$attackLevel = $ary['getHiscore']['attack']['lvl'];
If you
echo $attackLevel;
it would be 97.

Just pull the arrays out with a foreach loop and write some sql to insert the data. I don't know anything about your DB so if you want help there update your question please.
Simplest way to do this:
foreach($highscores as $highScoreType => $highScoreValues){
$rank = $highScoreValues['rank'];
$lvl = $highScoreValues['lvl'];
$totalXp = $highScoreValues['totalxp'];
//then it's up to you to put it into a table somehow.
}
$highScoreType will contain the values 'overall','attack',etc
This format of the foreach loop is extremely handy when iterating over an associative array (hash map/hash table, which is actually what all PHP arrays are underneath) will allow you to not only grab the value of the current pointed to entry, but also the key for that entry.
So, assume we have a single entry hashmap with key 'foo' and value 'bar'. If I want to show 'bar' I would simple
echo $map['foo'];//will output 'bar'
and if I put this into the above type of foreach loop
foreach($map as $fooKey => $barValue) {
echo $fooKey;//outputs 'foo'
echo $barValue;//outputs 'bar'
}

Related

Removing duplicates from merged array

I've been doing here some name processor, and I've run into small, kind of noob-ish problem.
I have CSV file with names and status, filters them only by 'Cool Ones" status, then i'm querying SQL, and getting another list of names that i have entered manually.
So here is code example, where i'm taking CSV file, filter, querying SQL, then it creates array, merges it and sorts alphabetically.
$nameFile = "names/$eid.csv";
$content = array_map('str_getcsv', file($nameFile));
$filteredData = array_filter($content, function($v){
return $v['1'] === 'Cool Ones'; },ARRAY_FILTER_USE_BOTH); //because in this file there are also 'Not Cool Ones'
$freePeople = array();
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = '$eid' ORDER BY 'guestName'");
$sth->execute();
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$listNames = array();
foreach($result2 as $row) {
$listNames[] = $row['0'];
$freeGuestName = $row['0'];
$freeGuestType = $row['1'];
}
$merged = array_merge($filteredData, $result2);
$sortedGuests = usort($merged, "sortGuestNames");
so my problem lies, that when outputing array, I'm getting duplicate results,
[50] => Array
(
[0] => John Down
[1] => Best Ones
)
[51] => Array
(
[0] => John Down
[1] => Cool Ones
)
Dunno what's next - i want that if my queried name is same as in this first CSV file, then hide this one, and show mine.
i was trying to unset key with
foreach($merged[$i]['0'] as $key => $value) {
if (in_array($merged[$i]['0'], $value)) {
unset($merged[$i]['0'][$key]);
}
}
but no luck, still outputing duplicates.
You can suggest better approach.
I've thought - maybe open CSV, query SQL and find my manual names - look up in opened CSV fields, append my status there, merge and push them to SQL database or new CSV file, where it could be outputted.
Thanks a lot!
A few things,
The thing we need to do is merge both arrays, but control which one overwrites the other. I am not sure if what you have does that now (in a reliable way) but one way to do that is to build 2 arrays. Both with the same structure, and the key as your unique field so we want this:
$csv = ['John Down' => ['John Down','Best Ones']];
$db = ['John Down' => ['John Down','Cool Ones']];
Then when we do array merge, the second argument will overwrite the first. So if we do
$csv = ['John Down' => ['John Down','Best Ones']];
$db = ['John Down' => ['John Down','Cool Ones']];
print_r(array_merge($csv, $db));
echo "\n";
print_r(array_merge($db, $csv));
Output:
// print_r(array_merge($csv, $db));
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Cool Ones
)
)
//print_r(array_merge($db, $csv))
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Best Ones
)
)
Sandbox
As you can see we can control which array is overwritten by the order we send them to array_merge in. The second one (or right one) overwrites the one to the left. So simply it reads from left to right.
So now what's the easiest way to get that structure from the DB? In PDO we can use FETCH_GROUP which takes the first column in the query and uses it as the top level key.
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = :eid GROUP BY guestName ORDER BY guestName");
//-- add `GROUP BY guestName` we don't want duplicates anyway
//-- no quotes see: ... ORDER BY 'guestName');
//-- use prepared statements
$sth->execute(['eid'=>$eid]);
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$result2 = array_column($result2, null, 0);
For the CSV you can build it that way when you read the file (by adding the key) and using fgetcsv or you can use this trick (also used above):
$csv = [['John Down','Best Ones']];
print_r(array_column($csv, null, 0));
Output
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Best Ones
)
)
Sandbox
Which should give you basically what we need, then it's a simple matter of using array_merge.
One thing to mention is if your DB or CSV are not unique, you'll get some duplicate removal there too, you may have to account for.
Removing duplicates is fine, but you want to make sure to remove the correct duplicates in a repeatable and robust way. Using array_merge we can control that no mater the order the rows come in from the DB and the file.
Summery
So if we put this all together, this is all you should need:
$nameFile = "names/$eid.csv";
$content = array_map('str_getcsv', file($nameFile));
$filteredData = array_filter($content, function($v){
return $v['1'] === 'Cool Ones';
},ARRAY_FILTER_USE_BOTH); //because in this file there are also 'Not Cool Ones'
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = :eid GROUP BY guestName ORDER BY guestName");
$sth->execute(['eid'=>$eid]);
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$listNames = array_column($result2, 0);
$merged = array_merge(array_column($filteredData, null, 0), array_column($result2, null, 0));
$sortedGuests = usort($merged, "sortGuestNames");
So instead of adding code in patching over an issue, we went to the root cause and fixed it there and reduced the code by a few lines. This will work provided your CSV is in the correct format. guestName, guestType
Cheers!
http://php.net/manual/en/function.array-column.php
array_column ( array $input , mixed $column_key [, mixed $index_key = NULL ] ) : array
array_column() returns the values from a single column of the input, identified by the column_key. Optionally, an index_key may be provided to index the values in the returned array by the values from the index_key column of the input array.
input A multi-dimensional array or an array of objects from which to pull a column of values from. If an array of objects is provided, then public properties can be directly pulled. In order for protected or private properties to be pulled, the class must implement both the __get() and __isset() magic methods.
column_key The column of values to return. This value may be an integer key of the column you wish to retrieve, or it may be a string key name for an associative array or property name. It may also be NULL to return complete arrays or objects (this is useful together with index_key to reindex the array).
index_key The column to use as the index/keys for the returned array. This value may be the integer key of the column, or it may be the string key name. The value is cast as usual for array keys (however, objects supporting conversion to string are also allowed).
Assuming that you need unique user name, following is the solution.
Create a new blank users array.
Loop over the users array.
Append the users to new users array.
The key should be user name.
Hence, every time the same user comes, he will overwrite the previous one, removing duplicate.
Code:
$users = [
['John Down', 'Best Ones'],
['John Down', 'Cool Ones']
];
$newUsers = [];
if (! empty($users)) {
foreach ($users as $user) {
$newUsers[$user[0]] = $user[1];
}
}
echo '<pre>';print_r($newUsers);echo '</pre>';
// Output:
Array
(
[John Down] => Cool Ones
)
I solved my case:
I removed second key from merged array, then unserialize it, and mapped only unique ones! Everything is now working!
$input = array_map("unserialize", array_unique(array_map("serialize", $merged)));
Sometimes i really enjoy asking help for you, because it makes me think! To think deeper than ussual.

Loop Columns in Recordset

Please understand this is not how I would have designed this, nor is it the proper way to do something like this.
However, since I was given this task, and have absolutely no way around the way the data is structured, I am tasked with working with what I was given.
I have a table structured as such:
ID
UniqueName
DisplayName
Tag1
Tag...
Tag27
Yes, I have 27 columns, Tag1 - Tag27
NOTE: This cannot be changed due to the data coming from 15 year old software that is not updated, nor is there any chance of it being updated. (Hence my dilema)
I am getting my resultset utilizing PDO's fetchAll(PDO::FETCH_ASSOC)
How can I loop over these Tag* columns, and just the Tag* columns, without having to type out rs[$i]['Tag#'] for each individual one, also noting that there may be instances where I could have 14 Tag columns` instead of the 27 I state in this example...
COLUMN CODE:
// prod_opts_value_fields_prefix = "Tag"
foreach ($rs[$i] as $key => $val) {
if ( strpos ( strtolower ( $key ), strtolower ( $prod_opts_value_fields_prefix ) ) !== false ) {
$vals .= $val . '|';
echo $val . '<br />';
}
}
Nothing is getting echo'd
You could run a foreach over the rs[$i] and check if the key contains 'Tag' with strpos and go from there.
Alternatively you could use just a for loop running on 1 to 27 (or any maximum Tag value) and have an if condition checking if rs[$i]['Tag$N'] (where N is the for loop counting variable) isset, if not break from the loop as that means you've gone through all possible Tags.
The second solution here does have the assumption Tags are always numbered sequentially and in ascending order.
If you need it in multiple places, I'd probably add a setting to a preferences array or a constant that you can easily re-use:
const TAG_COLUMNS = [
'Tag1',
...
'Tag27',
];
You can use that wherever you need it to loop over a result set.

PHP storing array column output from a function i call upon multiple times

I am trying to merge two parts of a code. To do that i have to store the echo'd array column output from a function which i call on
multiple times into an array or arrays on which I can perform computations.
Here's what i'm trying to do:
<?php
$searchquery[1] ="jupiter";
$searchquery[2] ="venus";
//can be multiple queries
include ('find.php');
for($i=0;$i<count($searchquery);$i++)
{
find($searchquery[$i]);
}
/find.php echoes back to me a MySQL query which then
creates a 2 dimensional array for me called
/$searchresult which looks like this as an example
t x (let's call first column t for example, and second x)
|1|3|
|1|4|
|2|6|
|4|8|
|7|1|
and it echoes it back to me, this works.
But, i need to use the first column (t) (11247) output from find.php
which was the result of the searchquery "jupiter",
and i need to store it as some sort of array in this current sheet,
theni need to store the "venus" searchquery which is let's say
t x
|1|3|
|2|4|
|3|4|
|4|6|
|5|4|
and store the first column (t) as an array in this current sheet.
I am trying to store the echos from the find.php function as arrays so that
i can perform the following operation in the current sheet:
$venusarrayt = array(1, 1, 2, 4, 7); //the manually defined
//$searchresult first column output from find.php which echos to me (t) (11247)
$jupiterarrayt = array(1, 2, 3,4,5); //the manually defined
//$searchresult first column output from find.php which echos to me (t) (12345)
//I need to perform the following operation and sum all of the t combinations
for($l=0;$l<count($venusarrayt);$l++){
for($s=0;$s<count($jupiterarrayt);$s++){
echo $venusarrayt[$l]+$jupiterarrayt[$s];
This part works! But i am having trouble though merging the echo'd $searchresult output into an array on which i can perform the above
for loops. In this example i am doing it by hand by typing into the php sheet "venusarrayt and jupiterarrayt".
I am sure there is some way to store echo'd array column results from a function which i call upon multiple times into an array, but i
haven't figured out yet how. Please help.
I hope this helps:
<?php
$searchquery[0] ="jupiter";
$searchquery[1] ="venus";
//can be multiple queries
include ('find.php');
$results=null;
for($i=0;$i<count($searchquery);$i++)
{
$temp=find($searchquery[$i]);
$results[$i]=$temp[t];
}
for($l=0;$l<count($results[1]);$l++){
for($s=0;$s<count($results[0]);$s++){
echo $results[1][$l]+$results[0][$s];
}
}
Clear solution for anyway of further working with your searched data can looks something like this:
$searchquery[1] ="jupiter";
$searchquery[2] ="venus";
$search_result = array(); $i=0;
foreach($searchquery as $current_query){
$current_result = FindResul($current_query);
// FindResult will be your function which process single query and returns result of it
$search_result[$i]['query'] = $current_query;
$search_result[$i]['result'] = $current_result;
$i++;
}
After the execution the code above you will have array 2-lvl array with clear and easy to work with structure. You can use it how you'd like to compare data or show it in the way you want.
Resulting array will have such structure:
$search_result[0]['query'] = 'jupiter';
$search_result[0]['result'] = '...jupiter resul';
$search_result[1]['query'] = 'venus';
$search_result[1]['result'] = '...venus result';
You have to create nested array to resolve the issue I would give a little example how to do?
$searchquery[1] ="jupiter";
$searchquery[2] ="venus";
for($i=1;$i<=count($searchquery);$i++){
$temp=find($searchquery[$i]);
$results[$i]=$temp[t];
}
$k=$i;
for($z=0;$z<=$i;$z++){
$total='';
for($p=0;$p<=$k;$p++){
$total=$total+$results[$p][$z];
}
echo $total;
echo "\n";
}
function find($word){
return array('t' => array('1', '2', '4', '7'));
}
and answer would be something like this:
Array
(
[1] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 7
)
[2] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 7
)
)
2
4
8
14
This solution will add all the n number of queries first result, second result and so on.......

PHP Matching value in an multi-dimensional array

I am looping between 2 times in 10 minute intervals, which works fine and outputs a time like so:
<?=$time->format('H:i')?>
I then am pulling data from the database of times I then want to see if the data in the loop matches whats coming out of the database. I have created a method to get me all the records from the database and outputs them into an array. I then wanted to use in_array to match them up then run the value through another method to get data about it. Problem is that it doesnt match up, problem being:
if (array_search($time->format('H:i'), $bookings))
echo "Match";
$booking is a multi-dimension array looking like:
Array (
[0] => Array ( [id] => 1 [time] => 12:00 )
[1] => Array ( [id] => 2 [time] => 15:00 )
...
)
Thanks in advance!
It would be much easier if you get the values directly from database. still if you want to process it in php, you can try with array_walk(). I am not sure about the syntax but should be something like
function search($value, $key, $needle)
{
array_search($needle, $value);
}
array_walk($bookings, 'search', $time->format('H:i'));
where $value will be your inner arrays.
Guys, please correct me if i am wrong with the syntax
It would probably be simpler to directly query the database accordingly - if you can - and than your query would be something like
select * from `table_name` WHERE `date_field` = $your_date
If that does not form a solution you can use array_walk as above or simply loop a little more:
foreach($bookings as $array) {
if(array_search($time->format('H:i'), $array)) {
echo 'match';
// If you don't want to keep searching use 'break'.
}
}

Storing PHP syntax in variables

Is it possible to store PHP syntax in variables for later use and repetition like this:
$ifStart = "if(";
$ifEnd = "){ echo 'Test'; }";
$ifStart 'A' == 'B' $ifEnd;
Edit: What I'm trying to accomplish is this:
I have 3 form fields, and when the PHP script is loaded, any of the three can be set. None can be set, two, one... So I need some way to determine how many are set and what to output according to that. That's why.
Edit: Right, so I have one HTML Select and two text input fields. My script checks if those fields are set (isset) and does some code accordingly, putting information into arrays etc. What I want to do now though, is to check if the variables have been set one by one, so I can output the correct results which I have stored in arrays.
New edit: This is obviously so hard to explain. But imagine a search engine where you decide which fields you'd like to fill out and then the script checks which fields are set and loops through the array with all the results, only gathering out the elements with sub-elements corresponding to the requested search, that's what I'm trying to achieve.
Here's the array design with AGE and COUNTY selected/set in the POST (hence why there's no [city] elements:
Array
(
[1] => Array
(
[id] => 1
[age] => 19
[county] => 4353
)
[2] => Array
(
[id] => 2
[age] => 20
[county] => 4020
)
[3] => Array
(
[id] => 3
[age] => 30
[county] => 4020
)
)
Still trying to figure out how to only select out a specific array element depending on -its- contents. For example, I have an array like this:
Array ( 1: [age][county], 2: [age][county], 3: [age], 4: [county], 5: [age][county] )
I'd then like to only select the IDs containing both age and county, in this example ID 1, 2 and 5.
Edit: It'll be similar to a SQL query: WHERE age AND county, only this is in an array
It is possible...
BUT
if you have to do it, there's definitely something wrong with your design!
[Edit after your edit]
Your edit shows me that I was right. What you're trying to do, can be accomplished in a better way!
So if I understand you correctly, you want to alter your output according to which form fields have been filled/answered by the user. So far you are storing some values from the $_POST array in another array. In order to generate your output, it would be best to loop over that array and concatenate strings, depending on what has been filled.
foreach ($myArray as $formField => $value)
{
//do something for each $formField, depending on the $value
}
If that still leaves you puzzled, comment here.
The way you wrote it, it would not work, you would need to use eval(). The use of eval() , is in most cases bad practice. That would not be the main problem though, the main problem is, that such code is hard to read, hard to debug as well as maintain and hard to document. All it all, it is bad practice and will lead to a bad solution and more problems.
One clean way (clean because it avoids eval()) to do relatively dynamic code would be to store either a function name or, after php 5.3, a function reference.
E.g. something like:
$callback = "truth_check";
$condition_result = ($a == $b);
if(is_callable($callback)){
$callback($condition_result);
}
See a running example here: http://ideone.com/1SBYS
In your case the callback could be a result to run, e.g. "print_message_on_true_input" as some comparison function and the input could be the result of a conditional tested anywhere in your code, e.g. $print = (false || $a == $b);
Give your specific use cases, though, because 90% of the time intended behavior can be acheived much less fragily without resorting to any dynamic code at all.
Is it possible? Yes, using eval.
Should you do it? NO NO NO NO PLEASE DON'T.
No. You can use functions instead.
function my_compare($a, $b, $symbol = '==') {
eval('$result = $a ' . $symbol . ' $b;');
if ($result) echo 'Test';
}
// ...
my_compare('A', 'B');
Keep in mind the comments above that warn you about this idea!
But you could probably do this:
$ifStart = "if(";
$ifEnd = "){ echo 'Test'; }";
eval( " {$ifStart} 'A' == 'B' {$ifEnd} " );
Ok, so what you need has little relation to the dynamic code involved in your original question, so I'm going to start over and propose a different approach entirely:
//INITIALIZE
//get your input variables, defaulting to null
$inputs = array('select'=>#$_REQUEST['select_name'], 'input1'=>#$_REQUEST['input_1_name'], 'input2'=>#$_REQUEST['input_2_name']);
// initialize your output variables
$out1 = $out2 = $out3 = null;
//MANIPULATIONS
//perform actions based on the presence of variables in the array
if($inputs['select'] == 'whatever'){
$out1 = 'select was '.htmlentities($inputs['select']);
}
// .... perform more manipulations, setting output strings ...
// OUTPUT SECTION
echo $out1.$out2.$out3;

Categories