I have 2 tables in a database, 1 of them are linked with a foreign key to the first one. Each row on table 1 is linked to multiple rows in table 2. I am trying to make a query that looks at a WHERE from table 2 and returns multiple rows from table 2 which are sorted into the rows they linked with in table 1 and then put this all into one big multi dimensional array, so it should work something like this:
$array[0][column_name][0] this would use row 1 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][0] this would use row 2 from table 1 and give me a the first result in the column called column_name
$array[1][column_name][3] this would use row 2 from table 1 and give me a the 4th result in the column called column_name
etc
How can I query this and store it in a 3 dimensional array using PHP.
I have tried to word this in as clear manner as possible, if you are unsure what I am asking, please comment and I will update my question to make it clearer.
Assume that we have two tables, Company and Employee:
Company
------------------
ID Company_Name
1 Walmart
2 Amazon.com
3 Apple
Employee
---------------------------------
ID Company_Id Employee_Name
1 1 Sam Walton
2 1 Rob Walton
3 1 Jim Walton
4 1 Alice Walton
5 2 Jeff Bezos
6 2 Brian T. Olsavsky
7 3 Steve Jobs
8 3 Tim Cook
The easiest way to envision a multi-dimensional (nested) array is to mimic the looping required to get it: outer loop is the company, inner loop is the employees:
// ignoring database access, this is just pseudo code
$outer = [];
// select id, company_name from company
foreach $companyResult as $companyRow {
// select * from employee where company_id = ? {$companyRow['id']}
$inner= [];
foreach $employee_result as $employeeRow {
$inner[] = $employeeRow; // ie, ['id'=>'1','Company_Id'=>'1','Employee_Name'=>'Sam Walton']
}
$outer[] = $inner;
}
print_r($outer);
// yields ====>
Array
(
[0] => Array
(
[0] => Array
(
[id] => 1
[Company_Id] => 1
[Employee_Name] => Sam Walton
)
[1] => Array
(
[id] => 2
[Company_Id] => 1
[Employee_Name] => Rob Walton
)
[2] => Array
(
[id] => 3
[Company_Id] => 1
[Employee_Name] => Jim Walton
)
[3] => Array
(
[id] => 4
[Company_Id] => 1
[Employee_Name] => Alice Walton
)
)
[1] => Array
(
[0] => Array
(
[id] => 5
[Company_Id] => 2
[Employee_Name] => Jeff Bezos
)
[1] => Array
(
[id] => 6
[Company_Id] => 2
[Employee_Name] => Brian T. Olsavsky
)
)
[2] => Array
(
[0] => Array
(
[id] => 7
[Company_Id] => 3
[Employee_Name] => Steve Jobs
)
[1] => Array
(
[id] => 8
[Company_Id] => 3
[Employee_Name] => Tim Cook
)
)
)
It is also possible to do if you use associative arrays. Consider the flat file that this query produces:
select company.id company_id, company.name company_name,
emp.id employee_id, emp.employee_name
from company
inner join employee on company.id = employee.company_id
-----
company_id company_name employee_id employee_name
1 Walmart 1 Sam Walton
1 Walmart 2 Rob Walton
1 Walmart 3 Jim Walton
1 Walmart 4 Alice Walton
2 Amazon.com 5 Jeff Bezos
2 Amazon.com 6 Brian T. Olsavsky
3 Apple 7 Steve Jobs
3 Apple 8 Tim Cook
Just use the primary IDs as the keys for your arrays:
$employeeList = [];
foreach($result as $row) {
$cid = $row['company_name'];
$eid = $row['employee_name'];
// avoid uninitialized variable
// $employeeList[$row['company_name']] = $employeeList[$row['company_name']] ?? [];
// easier to read version of above
$employeeList[$cid] = $employeeList[$cid] ?? [];
// assign it...
$employeeList[$cid][$eid] = $row;
}
Or, if you simply want each company row to hold an array of employee names,
$employeeList[$cid][] = $row['employee_name'];
The way that I've shown you is useful if you know the company_id and want to find the associated rows:
foreach($employeeList[2] as $amazon_guys) { ... }
But it's not at all useful if you're trying to group by employee, or some other field in the employee table. You'd have to organize the order of your indexes by your desired search order.
In the end, it's almost always better to simply do another query and let the database give you the specific results you want.
I have a column pack_size in a table called product_master_test. The problem that I am facing is that the pack_size is in mixed formats, there is no uniformity to it.
For example:
4 x 2kg (pack size should be 4)
48-43GM (pack size should be 48)
12 x 1BTL (pack size should be 12)
1 x 24EA (pack size should be 24)
I've been thinking about different approaches, but I can't think of anything that would work without having a lot of IF statements in the query/PHP code. Is there a solution that I am missing?
I do have the file in Excel, if there is an easier way to process it using PHP.
I am not including any code, as I'm not entirely sure where to start with this problem.
Using a regex to split the pack size could at least give you the various components which you can then (possibly) infer more from...
$packs = ["4 x 2kg","48-43GM","12 x 1BTL","1 x 24EA", "12 X 1 EA"];
foreach ( $packs as $size ) {
if ( preg_match("/(\d*)(?:\s+)?[xX-](?:\s+)?(\d+)(?:\s+)?(\w*)/", $size, $match) == 1 ) {
print_r($match);
}
else {
echo "cannot determine - ".$size.PHP_EOL;
}
}
(regex can probably be optimised, not my area of expertise). It basically splits it to be a number, some space with either a x or a - and then another number followed by the units (some text). The above with the test cases gives...
Array
(
[0] => 4 x 2kg
[1] => 4
[2] => 2
[3] => kg
)
Array
(
[0] => 48-43GM
[1] => 48
[2] => 43
[3] => GM
)
Array
(
[0] => 12 x 1BTL
[1] => 12
[2] => 1
[3] => BTL
)
Array
(
[0] => 1 x 24EA
[1] => 1
[2] => 24
[3] => EA
)
Array
(
[0] => 12 X 1 EA
[1] => 12
[2] => 1
[3] => EA
)
With the else part it should also give you the ones it cannot determine and perhaps allow you to change it accordingly.
You could present an associative array of all the strings from the table as keys corresponding with correct pack_size you desire.
$packsize = ["4 x 2kg" => 4, "48-43GM" => 48, "12 x 1BTL" => 12, "1 x 24EA" => 24]; //add all pack_sizes here
echo $packsize["4 x 2kg"]; // Output: 4
Now you could get the acutal pack size via the key of associative array. It could save some time you would spend making if/else conditions or switching the input. I'm not sure if there is something wrong with this approach, so correct me if so.
I'm trying to build arrays based off of two queries, one in mysql one on db2. I've built the main arrays, the first dealing with product info and the 2nd dealing with customer info but they have some values in common.
This is working as far as structure but I've been trying to build a final array to contain data from the initial arrays that will build a report.
For now, I'm just going to try and output a report or csv file for each sku_id from the first query, and the total number/ quantity from the 2nd query.
The 2nd query/array contain multiple attributes on a per product/customer basis.
(
[1] => 2
)
Array
(
[1] => 5
)
Array
(
[1] => 14
)
Basically, I pulled three records from the first table for sku_id '1' and matched those three records in db2 for the same sku. Each element corresponds to a customer, so this structure says "customer A had 2 of sku_id 1, customer B had 5 of sku_id 1 and customer C had 14 of sku_id 1'
For the purpose of the report, each line will be for each sku so I want to instead output this as:
array
(
[1] => 21
)
Showing that overall, sku_id 1 had a total of 21. So I still want to keep my initial arrays structured like they are, but I want to modify my final array to just give me line items for the report.
How can I do that?
$skuQuery = "
SELECT
sku_id,
dealer_id,
locations,
s.sku_group_id as groupID,
s.frame as frame,
s.cover1 as cover,
s.color1 as color,
start_date - interval 7 day as start_date
from products p
inner join skus s on p.sku_id = s.id
where curdate() between p.start_date and p.expire_date
group by sku_id, dealer_id
limit 3";
$skuRslt = mysqli_query($conn,$skuQuery);
while($skuRow = mysqli_fetch_assoc($skuRslt)){
$skuResult['sku_id'] = $skuRow;
$dealerQuery = "
SELECT
cstnoc,
sum(orqtyc) as TotalQTY
from table
where cstnoc = {$skuRow['dealer_id']}
AND framec = {$skuRow['frame']}
AND colr1c = {$skuRow['color']}
AND covr1c = {$skuRow['cover']}
AND extd2d >= " . str_replace('-', '', $skuRow['start_date']) . "
group by cstnoc, framec
";
$dealerRslt = odbc_exec($DB2Conn, $dealerQuery);
$dealerResult = [];
foreach($skuResult as $skuRow){
while($dealerRow = odbc_fetch_array($dealerRslt)){
if ( !isset($dealerResult[$dealerRow['CSTNOC']]) ) {
$dealerResult[$dealerRow['CSTNOC']] = 0;
}
$dealerResult[$dealerRow['CSTNOC']] += $dealerRow['TOTALQTY'];
}
}
$skuTots = array_fill_keys(array_unique(array_column($skuResult, 'sku_id')), 0);
foreach ($skuResult as $rec) {
$skuTots[$rec['sku_id']] += $dealerResult[$rec['dealer_id']];
}
print_r($skuTots);
}
UPDATE:
This is the print out for my first two arrays:
Array
(
[0] => Array
(
[sku_id] => 1
[dealer_id] => 1976
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2018-03-
)
[1] => Array
(
[sku_id] => 1
[dealer_id] => 5400
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-04-
)
[2] => Array
(
[sku_id] => 1
[dealer_id] => 11316
[locations] => 1
[groupID] => 1
[frame] => 1051
[cover] => 1150
[color] => 99
[start_date] => 2017-02-
)
)
Array
(
[0] => Array
(
[CSTNOC] => 1976
[TOTALQTY] => 2
)
[1] => Array
(
[CSTNOC] => 5400
[TOTALQTY] => 5
)
[2] => Array
(
[CSTNOC] => 11316
[TOTALQTY] => 14
)
)
And here is an example of my end result I'm trying to achieve:
In the below example, sku_id, groupID and location would come directly from the first query. Days is the number of days between start_date (from query 1) and curDate(). Qty is orqtyc from query 2 and Average is a calculation based on locations, days and qty. Each row is based on a different customer, so even though the below example is for one sku, the rows signify a different customer with different locations, qty, etc.
SKU_id | groupID | locations (n) | Days (x) | Qty(q) | Average (x/(q/n))
--------------------------------------------------------------------------------
123 1 3 120 15 24
123 1 2 12 6 4
The Report or CSV would then only show one line item for the above:
SKU | Group | Average Days | Total units sold
123 | 1 | 28 | 21
Basically, for every sku I'm getting those records and then I need to do those calculations by row, and get a total of those values for each sku. The report will only have one line item per sku.
I am trying to calculate the winning order of golfers when they are tied in a competition.
These golf competitions are using the "stableford" points scoring system, where you score points per hole with the highest points winning. Compared to normal golf "stroke play" where the lowest score wins (though this also has the countback system, only calculating the lowest score in the event of a tie...)
The rules are to use a "countback". i.e., if scores are tied after 9 holes, the best placed of the ties is the best score from the last 8 holes. then 7 holes, etc.
The best I can come up with is 2 arrays.
An array with all the players who tied in a given round. ($ties)
One which has the full score data in (referencing the database playerid) for all 9 holes. ($tie_perhole)
I loop through array 1, pulling data from array 2 and using the following formula to create a temporary array with the highest score:
$max = array_keys($array,max($array));
If $max only has 1 item, this player is the highest scorer. the loop through the first array is "by reference", so on the next iteration of the loop, his playerid is now longer in the array, thus ignored. this continues until there is only 1 playerid left in the first array.
However, it only works if a single player wins in each iteration. The scenario that doesn't work is if a sub-set of players tie on any iterations / countbacks.
I think my problem is the current structure I have will need the original $ties array to become split, and then to continue to iterate through the split arrays in the same way...
As an example...
The $ties array is as follows:
Array
(
[18] => Array
(
[0] => 77
[1] => 79
[2] => 76
[3] => 78
)
)
The $tie_perhole (score data) array is as follows:
Array
(
[18] => Array
(
[77] => Array
(
[9] => 18
[8] => 16
[7] => 14
[6] => 12
[5] => 10
[4] => 8
[3] => 6
[2] => 4
[1] => 2
)
[79] => Array
(
[9] => 18
[8] => 17
[7] => 15
[6] => 14
[5] => 11
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
[76] => Array
(
[9] => 18
[8] => 16
[7] => 14
[6] => 12
[5] => 10
[4] => 8
[3] => 6
[2] => 4
[1] => 2
)
[78] => Array
(
[9] => 18
[8] => 17
[7] => 15
[6] => 13
[5] => 11
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
)
)
So in this competition, player's 78 and 79 score highest on the 8th hole countback (17pts), so 1st and 2nd should be between them. Player 79 should then be 1st on the 6th hole countback (14pts, compared to 13pts). The same should occur for 3rd and 4th place with the 2 remaining other players.
There are other scenarios that can occur here, in that within a competition, there will likely be many groups of players (of different amounts) on different tied points through the leaderboard.
Also note, there will be some players on the leaderboard who are NOT tied and stay in their current outright position.
The basics of the working code I have is:
foreach ($ties as $comparekey => &$compareval) {
$tie_loop = 0;
for ($m = 9; $m >= 1; $m--) {
$compare = array();
foreach ($compareval as $tie) {
$compare[$tie] = $tie_perhole[$comparekey][$tie][$m];
}
$row = array_keys($compare,max($compare));
if (count($row) == 1) {
$indexties = array_search($row[0], $ties[$comparekey]);
unset($ties[$comparekey][$indexties]);
// Now update this "winners" finishing position in a sorted array
// This is a multidimensional array too, with custom function...
$indexresults = searchForId($row[0], $comp_results_arr);
$comp_results_arr[$indexresults][position] = $tie_loop;
$tie_loop++;
}
// I think I need conditions here to filter if a subset of players tie
// Other than count($row) == 1
// And possibly splitting out into multiple $ties arrays for each thread...
if (empty($ties[$comparekey])) {
break;
}
}
}
usort($comp_results_arr, 'compare_posn_asc');
foreach($comp_results_arr as $row) {
//echo an HTML table...
}
Thanks in advance for any helpful insights, tips, thoughts, etc...
Robert Cathay asked for more scenarios. So here is another...
The leaderboard actually has more entrants (player 26 had a bad round...), but the code i need help with is only bothered about the ties within the leaderboard.
Summary leaderboard:
Points Player
21 48
21 75
20 73
20 1
13 26
This example produces a $tie_perhole array of:
Array
(
[21] => Array
(
[75] => Array
(
[9] => 21
[8] => 19
[7] => 16
[6] => 14
[5] => 12
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
[48] => Array
(
[9] => 21
[8] => 19
[7] => 16
[6] => 13
[5] => 11
[4] => 9
[3] => 8
[2] => 5
[1] => 3
)
)
[20] => Array
(
[73] => Array
(
[9] => 20
[8] => 18
[7] => 16
[6] => 13
[5] => 11
[4] => 8
[3] => 6
[2] => 5
[1] => 3
)
[1] => Array
(
[9] => 20
[8] => 17
[7] => 16
[6] => 14
[5] => 12
[4] => 9
[3] => 7
[2] => 4
[1] => 2
)
)
)
In this example, the array shows that players 75 and 48 scored 21 points that player 75 will eventually win on the 6th hole countback (14pts compared to 13pts) and player 48 is 2nd. In the next tied group, players 73 and 1 scored 20 points, and player 73 will win this group on the 8th hole countback and finishes 3rd (18 pts compared to 17 pts), with player 1 in 4th. player 26 is then 5th.
Note, the $tie_loop is added to another array to calculate the 1st to 5th place finishing positions, so that is working.
Hopefully that is enough to help.
Ok, so I don't understand golf at all... hahaha BUT! I think I got the gist of this problem, so heres my solution.
<?php
/**
* Author : Carlos Alaniz
* Email : Carlos.glvn1993#gmail.com
* Porpuse : Stackoverflow example
* Date : Aug/04/2015
**/
$golfers = [
"A" => [1,5,9,1,1,2,3,4,9],
"B" => [2,6,4,2,4,4,1,9,3],
"C" => [3,4,9,8,1,1,5,1,3],
"D" => [1,5,1,1,1,5,4,5,8]
];
//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
$positions = array(); // The score numer is the key!
foreach ($golfers as $golfer=>$score ) { // Get key and value
$score_sub = array_slice($score,0,$hole); // Get the scores subset, first iteration is always all holes
$total_score = (string)array_sum($score_sub); // Get the key
if(!isset($positions[$total_score])){
$positions[$total_score] = array(); // Make array
}
$positions[$total_score][] = $golfer; // Add Golpher to score.
}
ksort($positions, SORT_NUMERIC); // Sort based on key, low -> high
return array(end($positions), key($positions)); // The last shall be first
}
//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
if ($hole == 0) return;
$winner = get_winners($golfers,$hole); // Get all ties, if any.
if(count($winner[0]) > 1){ // If theirs ties, filter again!
$sub_golfers =
array_intersect_key($golfers,
array_flip($winner[0])); // Only the Worthy Shall Pass.
$winner = getWinner($sub_golfers,$hole - 1); // And again...
}
return $winner; // We got a winner, unless they really tie...
}
echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";
Ok... Now ill explain my method...
Since we need to know the highest score and it might be ties, it makes no sense to me to maintain all that in separate arrays, instead I just reversed the
golfer => scores
to
Tota_score => golfers
That way when we can sort the array by key and obtain all the golfers with the highest score.
Now total_score is the total sum of a subset of the holes scores array. So... the first time this function runs, it will add all 9 holes, in this case theres 3 golfers that end up with the same score.
Array
(
[0] => Array
(
[0] => A
[1] => B
[2] => C
)
[1] => 35
)
Since the total count of golfers is not 1 and we are still in the 9th hole, we run this again, but this time only against those 3 golfers and the current hole - 1, so we are only adding up to the 8th hole this time.
Array
(
[0] => Array
(
[0] => B
[1] => C
)
[1] => 32
)
We had another tie.... this process will continue until we reach the final hole, or a winner.
Array
(
[0] => Array
(
[0] => C
)
[1] => 31
)
EDIT
<?php
/**
* Author : Carlos Alaniz
* Email : Carlos.glvn1993#gmail.com
* Porpuse : Stackoverflow example
**/
$golfers = [
"77" => [2,4,6,8,10,12,14,16,18],
"79" => [3,5,7,9,11,14,15,17,18],
"76" => [2,4,6,8,10,12,14,16,18],
"78" => [3,5,7,9,11,13,15,17,18]
];
//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
$positions = array(); // The score numer is the key!
foreach ($golfers as $golfer => $score) { // Get key and value
//$score_sub = array_slice($score,0,$hole); // Get the scores subset, first iteration is always all holes
$total_score = (string)$score[$hole-1]; // Get the key
if(!isset($positions[$total_score])){
$positions[$total_score] = array(); // Make array
}
$positions[$total_score][] = $golfer; // Add Golpher to score.
}
ksort($positions, SORT_NUMERIC); // Sort based on key, low -> high
return [
"winner"=> end($positions),
"score" => key($positions),
"tiebreaker_hole" => [
"hole"=>$hole,
"score"=> key($positions)],
]; // The last shall be first
}
//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
if ($hole == 0) return;
$highest = get_winners($golfers,$hole); // Get all ties, if any.
$winner = $highest;
if(count($winner["winner"]) > 1){ // If theirs ties, filter again!
$sub_golfers =
array_intersect_key($golfers,
array_flip($winner["winner"])); // Only the Worthy Shall Pass.
$winner = getWinner($sub_golfers,$hole - 1); // And again...
}
$winner["score"] = $highest["score"];
return $winner; // We got a winner, unless they really tie...
}
echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";
Result:
Array
(
[winner] => Array
(
[0] => 79
)
[score] => 18
[tiebreaker_hole] => Array
(
[hole] => 6
[score] => 14
)
)
I'm working with multi-dimesional arrays and am stumped trying to create a dynamic compatibility chart.
Given Data :
4 instances of Value 1
7 instances of Value 2
Ideal End Result Examples
If there are 4 value 1's and 7 value 2's
[0] Value 2
[1] Value 1
[2] Value 2
[3]
[4] Value 2
[5] Value 1
[6] Value 2
[7]
[8] Value 2
[9] Value 1
[10] Value 2
[11] Value 1
[12] Value 2
If there are 4 value 1's and 4 value 2's (try to evenly space them out without overlapping)
[0] Value 1
[1] Value 2
[2]
[3] Value 1
[4] Value 2
[5]
[6]
[7] Value 1
[8] Value 2
[9]
[10]
[11] Value 1
[12] Value 2
If there are 2 instances of 1 and three of 2.
[0] Value 1
[1] Value 2
[2]
[3]
[4]
[5]
[6] Value 2
[7]
[8]
[9]
[10]
[11] Value 1
[12] Value 2
I have bucket sizes of 12-24 for an array. In the example I use 12. If number of instances don't fit in 12 buckets, can move up all the way to 24. If it doesn't fit in 24, give an error.
Any loop that I've tried to make by using array_pop and reversing arrays creates empty holes in the middle of the array or it doesn't spread out the instances evenly.
Edit:
Here is what I've tried.
$table_array = range(0,12);
// Method 1
for ($i = 0; sizeof($table_array); $i++)
{
$ready_array[] = ($i % 2) ? array_pop($table_array) : array_shift($table_array);
}
// Method 2
for ($i = 0; $i < sizeof($table_array); $i++)
{
$index = ($i % 2) ? sizeof($table_array) - ceil($i / 2) : ceil($i / 2);
$ready_array[$index] = $table_array[$i];
}
ksort($ready_array);
Project Details and Goal
I have a list of compatible and incompatible pills.
I let the users select which pills and how many they're taking.
Based on that, I search the database and match up pills based on compatibility creating a lists known as Value 1 and Value 2.
I have a minimum 12 hour time frame to take the pills.
I'm supposed to space out lets say 4 of Calcium and 3 of Iron evenly across 12 hour period with 1 hour intervals and not let them overlap. If they choose 12 pills of Calcium and 12 pills of Iron, I can push up to 24 hours to make them take those pills.
Some ideas for your question :
Use array_chunk() and array_count_values()
<?php
$myarray = array(2,1,2,'',2,1,2,'',2,1,2,1,1,2,1,2,'',2,1,2,'',2,1,2,1,2);
$tests = array_chunk($myarray, 13);
//print_r($tests);
function mytest($tests)
{
foreach($tests as $test)
{
// play your test !
print_r(array_count_values($test));
}
}
// add your argument for your test
echo mytest($tests);
?>
output :
Array
(
[2] => 6
[1] => 5
[] => 2
)
Array
(
[2] => 7
[1] => 4
[] => 2
)
See for test : http://codepad.org/M9u5G0je