new to php, I have read dozens of 'insert data into arrays' posts on the net, but none seem to describe my exact problem, (or I am too ignorant to recognize it) So here we go.
I get some data from a SQL database. I want to present this in a table. I have a working script that does this, but it has a slight malfunction, it doesn't show any empty results (rows) in the table. To get the desired result I my attempt 2.0 starts now with an empty table, with all the rows, and fill them in with data later.
$names = array('Alice', 'Bob', 'Charles', 'Dick', 'Emy');
for ($i=0; $i< count($names); $i++)
{
$empty_table[] = array("Name" => $names[$i], "Total" => 0, "Wins" => 0, "Losses" => 0) ;
}
This gives me an array that I can display as a table, with 5 data rows, each containing a value for the field with a Name key (Alice to Emy) and otherwise filled with values set to 0 for all other keys. It looks like:
Name Total Wins Losses
Alice 0 0 0
Bob 0 0 0
Charles 0 0 0
Dick 0 0 0
Emy 0 0 0
From the database I want to fill in the values that are 0, IF and only IF there is something in the database. I get the data with
for ($i=0; $i< count($names); $i++) {
$sql = "SELECT
SEVERAL THINGS
WHERE Something = '".$names[$i]."'";
$result = mysqli_query($conn,$sql);
// Not the real query obviously, but that part works and gives the intended result.
while ( $row = mysqli_fetch_assoc($result) ) {
Do something with the result in $row;
} // end while
} // end for
$row is an array with the same structure as one of the values of $empty_table. Example:$row could be: Array ( [Name] => Bob [Total] => 10 [Wins] => 7 [Losses] => 3 )
Now the question. How do I get the one dimension array $row into the two dimension array $empty_table (replacing the '0' values) , given that $empty_table always has a fixed 5 elements but there can be any number between 0 and 5 '$row's in the while loop depending on what is in the database?
If I try:
$empty_table[] = $row;
or
$empty_table[$row['Name']] = $row;
I only get rows added to the existing array with '0' values, not rows replaced. I do understand that the solution is between the '[]' but I can't figure out put something there that relates to the proper $row[].
I think that you can change the array construction to this:
for ($i=0; $i< count($names); $i++) {
$empty_table[$names[$i]] = array("Name" => $names[$i], "Total" => 0, "Wins" => 0, "Losses" => 0) ;
}
So your array keys will be the name of the person. With this you can identify what row is each moment. The array should be constructed like this:
$empty_table = Array(
"John"=> Array("name"=>"John", "total"=>10, "wins"=>3, "loses"=>7),
"Mary"=> Array("name"=>"Mary", "total"=>10, "wins"=>5, "loses"=>5),
);
So you can access to the values like this:
$john_row = $empty_table["John"];
I am just pretending that the $row is from db.
<?php
$names = array('Alice', 'Bob', 'Charles', 'Dick', 'Emy');
for ($i=0; $i< count($names); $i++)
{
$empty_table[] = array("Name" => $names[$i], "Total" => 0, "Wins" => 0, "Losses" => 0) ;
}
$row = array("Name" => 'Alice', "Total" => 5, "Wins" => 6, "Losses" => 4);
$found = false;
foreach($empty_table as $k=>$a) {
if($a['Name'] == $row['Name']) {
$found = true;
break;
}
}
if($found) {
echo 'index found'.$k.'\n';
$empty_table[$k] = array_replace($empty_table[$k], $row);
var_dump($empty_table);
}
Related
I have a CSV file:
name;categories;data
019;07:50:00;0,0
017;07:50:00;0,8
019;07:55:00;0,4
017;07:55:00;1,3
019;08:00:00;0,8
017;08:00:00;1,9
I would like to convert it using PHP into a JSON file according to the following scheme (for a Highcharts diagram):
{
"xAxis":
{
"categories": ["07:50:00","07:55:00","08:00:00"]
},
"series":
[
{
"name": "019",
"data": [0.0,0.4,0.8]
},
{
"name": "017",
"data": [0.8,1.3,1.9]
}
]
}
Explanation:
The rows of the CSV file alternate with data records, here: 019 and 017 (column: 'name'). There can also be more than two data records, this is not fixed. Every five minutes (column: 'categories') the value changes (column: 'data').
I think this is done with the programming method called 'control break'. Can anyone show me an approach to how it could work?
This can be done with a single loop, ergo it should be done with a single loop.
Code: (Tested to be successful on localhost)
if (($handle = fopen("Zurreal.csv", "r")) !== false) {
fgetcsv($handle, 1000, ';'); // disregard column heading row
while (($row = fgetcsv($handle, 1000, ';')) !== false) { // notice the semicolon delimiter
if (!isset($firstname)) { // if the first row of data
$firstname = $row[0]; // cache the name value
$xAxis['xAxis']['categories'] = [$row[1]]; // store this row's time value
} elseif ($firstname == $row[0]) {
$xAxis['xAxis']['categories'][] = $row[1]; // store only time values from rows with first row's name
}
$series[$row[0]]['name'] = $row[0]; // store (and overwrite after first occurrence) the name value
$series[$row[0]]['data'][] = (float)str_replace(',', '.', $row[2]); // push the prepared data values into the data subarray
}
fclose($handle);
$result = array_merge($xAxis, ['series' => array_values($series)]); // remove the temp keys from series and build the final array structure
echo json_encode($result, JSON_PRETTY_PRINT); // convert final array to json (pretty print for demo only)
}
Output:
{
"xAxis": {
"categories": [
"07:50:00",
"07:55:00",
"08:00:00"
]
},
"series": [
{
"name": "019",
"data": [
0,
0.4,
0.8
]
},
{
"name": "017",
"data": [
0.8,
1.3,
1.9
]
}
]
}
Less of a control break implementation actually, because working with arrays makes the usual checks and looking back at the “previous record” key value superfluous.
Here’s how I would do this,
<?php
// using a data array here for illustration purposes,
// replace with reading from CSV on your own
$data = array (
0 =>
array (
0 => '019',
1 => '07:50:00',
2 => '0,0',
),
1 =>
array (
0 => '017',
1 => '07:50:00',
2 => '0,8',
),
2 =>
array (
0 => '019',
1 => '07:55:00',
2 => '0,4',
),
3 =>
array (
0 => '017',
1 => '07:55:00',
2 => '1,3',
),
4 =>
array (
0 => '019',
1 => '08:00:00',
2 => '0,8',
),
5 =>
array (
0 => '017',
1 => '08:00:00',
2 => '1,9',
),
);
// initialize object and properties to hold final result
$result = new stdClass();
$result->xAxis = new stdClass();
$result->series = [];
$categories = $series = [];
// accumulate data for categories and series
// using the name as key for the series array, makes it easier - otherwise you’d
// have to check if an array entry with specific name property value already exists
foreach($data as $row) {
$categories[] = $row[1];
$series[$row[0]][] = $row[2];
}
// make categories unique to filter out duplicates, and re-index numerically
$result->xAxis->categories = array_values(array_unique($categories));
// transform temporary series data into final structure, putting the key into the
// name property now
foreach($series as $key => $item) {
$temp = new stdClass();
$temp->name = $key;
$temp->data = $item;
$result->series[] = $temp;
}
echo json_encode($result);
(Transformation of the x,y values in the last CSV column to x.y integer format I’ll also leave up to you.)
TL;DR
Sort/group array by key without adding another level to the array (data parsed by jQuery plugin)?
Details
I am building an array to return to some <select> DOM element.
It takes in CC (engine size stuff) as a parameter and uses that as a key, the problem lies with sorting the array after.
Let's say, user selects this range of CC's:
50, 100, 125
50 has 32 options available
100 has 3 options available
125 has 12 options available
My current code loops through the CC's, executes the SQL to get the options and using a loop counter creates the key like this:
$options[$cc. $id] = $someValue;
This works as you'd expect, however my output is showing results not in exactly the order I need (CC ASC - so all 50s should show first, together).
The problem is that
50 with 32 goes upto 5031 as a key.
100 with 3 goes upto 1002 as a key.
125 with 12 goes upto 12511 as a key.
By now hopefully you can clearly see the issue. 5031 is greater than 1002. So options for 50cc with a loop counter passed 9 is greater than 100cc options.
(just for clarity, example output is):
50cc Option 1
50cc Option 2
50cc Option 3
50cc Option 4
50cc Option 5
100cc Option 1
100cc Option 2
100cc Option 3
50cc Option 6
50cc Option 7
Maybe the initial problem is how I'm creating the keys, but I've tried to use ksort with a few different flags to try and achieve my goal but none of the flags seem to target what I'm after:
SORT_REGULAR - compare items normally (don't change types)
SORT_NUMERIC - compare items numerically
SORT_STRING - compare items as strings
SORT_LOCALE_STRING - compare items as strings, based on the current locale. It uses the locale, which can be changed using setlocale()
SORT_NATURAL - compare items as strings using "natural ordering" like natsort()
SORT_FLAG_CASE - can be combined (bitwise OR) with SORT_STRING or SORT_NATURAL to sort strings case-insensitively
How do I sort/group my keys without adding another level to my array (the data is parsed by a jQuery plugin that needs the data in a certain format)?
EDIT: Full Script
<?php
if (strpos(PHP_OS, 'Linux') > -1) {
require_once $_SERVER['DOCUMENT_ROOT']. '/app/connect.php';
} else {
require_once getcwd(). '\\..\\..\\..\\..\\app\\connect.php';
}
$make = $_POST['make'];
$cc = $_POST['cc'];
$sql = 'SELECT * FROM `table`
WHERE `UKM_CCM` = :cc
AND `UKM_Make` = :make
ORDER BY `UKM_Model`, `UKM_StreetName`, `Year` ASC;';
$options = array();
foreach ($cc as $k => $value)
{
$res = $handler->prepare($sql);
$res->execute(array(':cc' => $value, ':make' => $make));
$data = $res->fetchAll(PDO::FETCH_ASSOC);
$i = 0;
if (count($data) > 0) {
foreach ($data as $result)
{
$arrayKey = sprintf('%03d%02d', $cc, $i);
$epid = $result['ePID'];
$make = $result['UKM_Make'];
$model = $result['UKM_Model'];
$cc = $result['UKM_CCM'];
$year = $result['Year'];
$sub = $result['UKM_Submodel'];
$street = $result['UKM_StreetName'];
$options[$arrayKey]['name'] = $make. ' ' .$model. ' ' .$cc. ' ' .$year. ' ' .$sub. ' ' .$street;
$options[$arrayKey]['value'] = $epid;
$options[$arrayKey]['checked'] = false;
$options[$arrayKey]['attributes']['data-epid'] = $epid;
$options[$arrayKey]['attributes']['data-make'] = $make;
$options[$arrayKey]['attributes']['data-model'] = $model;
$options[$arrayKey]['attributes']['data-cc'] = $cc;
$options[$arrayKey]['attributes']['data-year'] = $year;
$options[$arrayKey]['attributes']['data-sub'] = $sub;
$options[$arrayKey]['attributes']['data-street'] = $street;
$i++;
}
}
}
ksort($options, SORT_STRING);
echo json_encode($options);
You could format the key to have 3 digits for the cc and 2 for the option...
$options[sprintf('%03d%02d', $cc, $id)] = $someValue;
which should give you keys 05031 and 10002.
Then use SORT_STRING to force it to sort them as strings (although they would sort as numbers as well)
If you can add the key additional into your array, you could create a usort() that will sort your array as you need it:
$arrSort = [50, 100, 125];
$arrData = [
501 => [
'foo',
'bar',
'arrayKey' => 501
],
504 => [
'foo',
'bar',
'arrayKey' => 504
],
1002 => [
'foo',
'bar',
'arrayKey' => 1002
],
10045 => [
'foo',
'bar',
'arrayKey' => 10045
],
1251 => [
'foo',
'bar',
'arrayKey' => 1251
],
5045 => [
'foo',
'bar',
'arrayKey' => 5045
]
];
usort($arrData, function($a, $b) use ($arrSort)
{
$posA = array_search(substr($a['arrayKey'], 0, 2), $arrSort);
if ($posA === false) {
$posA = array_search(substr($a['arrayKey'], 0, 3), $arrSort);
}
$posB = array_search(substr($b['arrayKey'], 0, 2), $arrSort);
if ($posB === false) {
$posB = array_search(substr($b['arrayKey'], 0, 3), $arrSort);
}
return $posA - $posB;
});
The return of this function in this example would be:
array:6 [▼ 0 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 501 ] 1 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 504 ] 2 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 5045 ] 3 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 1002 ] 4 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 10045 ] 5 => array:3 [▼
0 => "foo"
1 => "bar"
"arrayKey" => 1251 ] ]
Make your 'faux delimiter' an uncommon pattern like "929292" in this example. Then you can use uksort to go through just your keys. replace "929292" with "." so you end up with something like "100.3", "125.12" and "150.32".
Now you're no longer limited to trying to work numerically and with patterns, you can use a good old explode and compare manually.
This solution doesn't care what's nested in your arrays, it's only concerned with the keys for sorting.
$options = [
'1009292923' => '100cc',
'12592929212' => '150cc',
'5092929232' => '50cc'
];
$sorted = uksort($options, function($a, $b){
$parts = explode('.',str_replace('929292', '.', $a));
$acc = $parts[0];
$parts = explode('.',str_replace('929292', '.', $b));
$bcc = $parts[0];
if($acc == $bcc) { return 0; }
if($acc > $bcc) { return 1; }
if($acc < $bcc) { return -1; }
});
var_dump($options);
Edit: str_replace is kind of pointless here, you could just run explode directly on the key using "929292" as your delimeter.
I'm not sure this could be an exact answer to my own question as it handles the issue but in a different way. Essentially, I've changed my SQL to this:
$sql = 'SELECT * FROM `ebay_mml`
WHERE `UKM_CCM` IN ('. $where .')
AND `UKM_Make` = :make
ORDER BY CAST(SUBSTR(`UKM_CCM`, INSTR(`UKM_CCM`, " ") + 1) AS UNSIGNED),
`UKM_Model`,
`UKM_StreetName`,
`Year`
ASC;';
$where is a variable generated from a foreach loop:
foreach ($cc as $k => $v)
{
$where .= ':'. $k .($k != end(array_keys($cc)) ? ', ' : '');
$whereData[':'. $k] = $v;
}
This returns all my data at once, so all I need to do now is loop through the results and count the iterations as I go to build the key:
$i = 0;
foreach ($data as $result)
{
# my sexy code
$i++;
}
Now my results are as I want them.
Disclaimer: As this does resolve the issue at hand, it kinda veers away from the original question posed as it's more of a MySQL solution as opposed to sorting/grouping the array by it's key value. Let me know if this answer is ok (if so, will remove the disclaimer segment) or not.
Thanks for your help all :)
I have a query which converts item rows of a series of IDs in MySQL table to columns, so the result has variable number of columns (VC). The only items that are not dynamic are the ID, FirstName, LastName. I know the number of variable items (n), which is part of the query.
what I want to do is to have a loop inside PHP while to add these variable columns to the PHP array.
something like this:
$someArray = [];
while($row = $result -> fetch_assoc()) {
array_push($someArray,[
'ID' => $row['EmployeeID'],
'FName' => $row['FName'],
'MName' => $row['MName'],
'LName' => $row['LName'],
-------Loop here --------
'VC1' => $row['VC1'],
'VC2' => $row['VC2'],
'VC3' => $row['VC3'],
'VC4' => $row['VC4'],
'VC5' => $row['VC5'],
..............
'VCn' => $row['VCn']
-------------------------
]);
}
I tried with PHP loop and could not figure out how to do it.
Thanks for any help in advance.
If you know n you can use for loop:
for($i = 1;$i <= $n; $i++) {
$key = "VC" .$i;
$someArray[$key] = $row[$key];
}
However, if you pushing the entire array in it may be better do modify your SQL query and do just:
while($row = $result -> fetch_assoc())
array_push($someArray, $row)
I have the following array:
fruits = [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"green", "name"=>"kiwi"],
3 => ["color"=>"red", "name"=>"carrot"],
4 => ["color"=>"yellow", "name"=>"banana"],
5 => ["color"=>"yellow", "name"=>"mango"],
];
And I need to get it into the following form:
fruits = [
0 => [
0 => ["color"=>"red", "name"=>"apple"],
1 => ["color"=>"red", "name"=>"tomato"],
2 => ["color"=>"red", "name"=>"carrot"]
],
1 => [
0 => ["color"=>"yellow", "name"=>"banana"],
1 => ["color"=>"yellow", "name"=>"mango"],
],
2 => [
0 => ["color"=>"green", "name"=>"kiwi"],
]
];
That is I need to group on the basis of color of the fruit.
I tried but somehow I can not get it correct.
An associative array would be simple :
$sortedFruits = array();
for ($i = 0; $i < count($fruits); $i++) {
if(!$sortedFruits[$fruits[$i]['color']]) { $sortedFruits[$fruits[$i]['color']] = array(); }
array_push($sortedFruits[$fruits[$i]['color']], $fruits[$i]);
}
print_r($sortedFruits['red']);
One option is to loop the colors by extracting names and colors to seperate flat arrays.
This way you only loop the count of unique colors (in this case three times).
Then you do the matching with array_intersect.
$colors = array_column($fruits, "color");
$names = array_column($fruits, "name");
foreach(array_unique($colors) as $color) $new[$color] = array_intersect_key($names, array_intersect($colors, [$color]));
var_dump($new);
https://3v4l.org/5pODc
You're overcomplicating the resulting array, I would suggest to replace first level key with color name and just go with names in each of them.
Building a code logic for your example will be way to big and it doesn't need to be.
I'm creating a php file that displays 4 event sponsor ads. For each sponsor I created an element in an array for their id, name, URL to site, URL for image, level of sponsor, value of sponsorship
I want to create a randomizer that will randomly populate 4 records from the array, but weight the values so that higher level sponsors appear more often than others. I've already created the value for the weight as a percent.
The closest solution I've found to this problem is:
MySQL: Select Random Entry, but Weight Towards Certain Entries
Which suggests including this code, BUT the referenced link for the similar issue doesn't make sense to me. It suggests using ORDER BY -LOG(1.0 – RAND()) / Multiplier but I'm using an array, not record results and I'm not completely clear on how this works...
It looks like array_rand will be helpful to at least generate the 4 different values, but still not sure how to weight them.
You may use a custom function to sort the array based on the weight of sponsor. See usort.
usort($data, function ($value) {
return rand(0, 100 - $value['weight']);
});
Example:
$data = array(
array('name' => 'low', 'weight' => 5),
array('name' => 'medium', 'weight' => 35),
array('name' => 'high', 'weight' => 60)
);
$frequency = array();
for ($i = 0; $i < 1000; $i++) {
usort($data, function ($value) {
return rand(0, 100 - $value['weight']);
});
$head = reset($data);
if (!isset($frequency[$head['name']])) {
$frequency[$head['name']] = 0;
}
$frequency[$head['name']]++;
}
print_r($frequency);
/*
Array
(
[low] => 263
[medium] => 328
[high] => 409
)
*/
This ORDER BY statement will not be executed by your application, it rather tells the DBMS to order the results returned by your query. You add a LIMIT 4 to the query and you're set.
EDIT
Oh, I just read that you do not use a database.
EDIT 2
To really answer your question:
Iterate over your full array
Calculate each item's result using the above equation and store that along with the item's key/index in a temporary array
Sort the temporary array (highest value first), then trim it down to X items
You then have the keys/indexes of X items which were randomly chosen
OK, I finally worked everything out!
Here is how my final code went (I've separated and simplified the code here to piece-meal the tasks):
Create the array
$Sponsors=array(
array('Sponsor' => 'Sponsor1', 'Value' => '500'),
array('Sponsor' => 'Sponsor2', 'Value' => '300'),
array('Sponsor' => 'Sponsor3', 'Value' => '300'),
array('Sponsor' => 'Sponsor4', 'Value' => '200'),)
);
Set SponsorTotalCt = the number of sponsors and create a variable to hold the weighted percentage
$SponsorTotalCt = count($Sponsors);
$SponsorWeight = 0;
Get a total value for all the sponsorships
$SponsorTotalAmt = 0;
foreach($Sponsors as $array)
{
$SponsorTotalAmt += $array['Value'];
};
Add the sponsor weight as a percent as another 'field value' in the $Sponsors array
$i = 0; //initialize i
while ($i < $SponsorTotalCt){
foreach($Sponsors as $array)
{
$SponsorWeight = round($Sponsors[$i]['Value']/$SponsorTotalAmt * 100);
$Sponsors[$i]['Weight'] = $SponsorWeight;
};
$i++; // count one up
};
*Note: at this point the $Sponsors 'record listing' would look kind of like this
$Sponsors =
[0] 'Sponsor1','500', 38 (this last is the percent the sponsorship should be weighted)
[1] 'Sponsor2', '300', 23
[2] 'Sponsor3', '300', 23
[3] 'Sponsor4', '200', 15
Notice that the last set of values adds up to 100, or close to it (because of the rounding)*
Create a new array of 100 'records' where each row of the $Sponsors array is repeated the number of times that reflects the percentage. i.e. Sponsor1 values will be repeated in the array 38 times
$newSponsors = array();
$i = 0; //initialize i
while ($i < $SponsorTotalCt){
foreach($Sponsors as $array)
{
$a = array_fill(0, $Sponsors[$i]['Weight'], $Sponsors[$i]);
};
$newSponsors= array_merge($newSponsors,$a);
$i++; // count one up
};
Finally, randomly select 3 keys from the 4 Sponsors, weighted by the value of their Sponsorships
$spot = array_rand($newSponsors,3);
Now I only have to create the code and call the value. YAY!