Add non existings columns to mysql query result - php

I have a PHP variable $members and it looks like this
$members = [
[
"name" => "John",
"age" => 23
],
[
"name" => "Sasha",
"age" => 24
],
[
"name" => "Asmita",
"age" => 23
]
];
Now my application stores rates based on the age of people so there is a rates table with the following structure
id
age
price
1
23
200
2
24
230
3
25
250
So, I am looping through all members and get this final query
SELECT * FROM rates WHERE age =23 OR age=24 OR age = 23
It returns me the following result
id
age
price
1
23
200
3
24
230
I want to know if there is some way I can add the member to the result set so it returns results for all 3 members like this using MYSQL itself
id
member
age
price
1
John
23
200
2
Sasha
24
230
1
Asmita
23
200

SQL cannot read your PHP variable. It can only read data from tables in the database, or literal values in the query. So if your data is only in the PHP variable $members (i.e. not stored in the database), this will get tricky.
The easiest solution is to query your rates table into another PHP variable, and then merge them. You can limit the result to the age values in your input data.
<?php
# form input from $_POST
$members = [
[
"name" => "John",
"age" => 23
],
[
"name" => "Sasha",
"age" => 24
],
[
"name" => "Asmita",
"age" => 23
]
];
# make list of ages appearing in the input
$ages = array_map(function($m) { return($m['age']); }, $members);
# list of '?' placeholders, equal in length to the number of ages
$placeholders = implode(',', array_fill(1, count($ages), '?'));
# connect to database
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
# fetch the rates for all specified ages
$stmt = $pdo->prepare("SELECT age, price from rates WHERE age IN ($placeholders)");
$stmt->execute($ages);
$ratesResult = $stmt->fetchAll();
# transform the result into an associative array, mapping age to price
$rates = [];
foreach ($ratesResult as $r) {
$rates[$r['age']] = $r['price'];
}
# merge the rates into the original array of input data
foreach ($members as &$m) {
$m['price'] = $rates[$m['age']];
}
print_r($members);
Output:
Array
(
[0] => Array
(
[name] => John
[age] => 23
[price] => 200.00
)
[1] => Array
(
[name] => Sasha
[age] => 24
[price] => 230.00
)
[2] => Array
(
[name] => Asmita
[age] => 23
[price] => 200.00
)
)

This should do what you want;
$members = [
[
"name" => "John",
"age" => 23
],
[
"name" => "Sasha",
"age" => 24
],
[
"name" => "Asmita",
"age" => 23
]
];
$ages = collect($members)->pluck('age')->unique();
$rates = Rate::whereIn('age',$ages)->get();
$merged = collect($members)->map(function($member) use($rates) {
$member['price'] = $rates->where('age', $member['age'])->first()->price;
return $member;
});
Gets the unique ages, then queries the database for the rates, then maps over the input array, adding in the price to each member

Related

PHP - Get latest entry for each element ID in assoc array [duplicate]

This question already has answers here:
Group rows of data by one column and find max value in another column within each group
(4 answers)
Closed 7 months ago.
I have some json data which I've decoded into an assoc array. This array contains a set of names, their respective ids, some relevant data and year. Here is a sample of my data:
Name
ID
Year
Gary
1
2016
Miller
2
2018
Spike
3
2019
Miller
2
2020
Gary
1
2018
Miller
2
2019
Gary
1
2017
Spike
3
2020
I have sorted this in descending order of year, but I would also like to retrieve all 3 IDs by their latest entry only. Below is my expected output:
Name
ID
Year
Miller
2
2020
Spike
3
2020
Gary
1
2018
I am not clear on how to get the latest entry from each respective ID.
One of the easier ways to do this is to loop through the sorted array and extract the record with the first instance of each ID into another array, keyed by the ID so we know when we have already found that ID.
<?php
$data = [
['Name' => 'Gary', 'ID' => 1, 'Year' => 2016],
['Name' => 'Miller', 'ID' => 2, 'Year' => 2018],
['Name' => 'Spike', 'ID' => 3, 'Year' => 2019],
['Name' => 'Miller', 'ID' => 2, 'Year' => 2020],
['Name' => 'Gary', 'ID' => 1, 'Year' => 2018],
['Name' => 'Miller', 'ID' => 2, 'Year' => 2019],
['Name' => 'Gary', 'ID' => 1, 'Year' => 2017],
['Name' => 'Spike ', 'ID' => 3, 'Year' => 2020]
];
// Sort all records by year in descending order
usort($data, function ($a, $b)
{
return $b['Year'] <=> $a['Year'];
});
// Create a temporary array for our unique entries. We will use the IDs as the keys
$latestEntryBuffer = [];
// Loop through the sorted data
foreach ($data as $currRow)
{
/*
Set a var for the ID, less typing, less chance for error,
our IDE will let us know if we mistype it somewhere
*/
$currID = $currRow['ID'];
/*
If this ID does not have a record in the buffer yet, set
the current record in the buffer. Since we sorted all data
by year descending, the first instance of the ID we encounter
will be the most recent
*/
if (!array_key_exists($currID, $latestEntryBuffer))
{
$latestEntryBuffer[$currID] = $currRow;
}
}
// Put all of the rows from the buffer into a simple array
$lastestEntries = array_values($latestEntryBuffer);
print_r($lastestEntries);
echo PHP_EOL;
Output:
Array
(
[0] => Array
(
[Name] => Miller
[ID] => 2
[Year] => 2020
)
[1] => Array
(
[Name] => Spike
[ID] => 3
[Year] => 2020
)
[2] => Array
(
[Name] => Gary
[ID] => 1
[Year] => 2018
)
)
Alternatively you could sort by year ascending and blindly assign each record to the buffer by ID, the last record for each ID would be the most recent.

how to compare 2 array and compare difference of same id in php?

this questin is asked many times but every one using same array but in my case i have 2 arrays
consider i have 2 arrays
array1:3 [
10 => 900.0
20 => 450.0
30 => 600.0
]
array2:3 [
30 => 200.0
10 => 500.0
20 => 600.0
]
output should be
[900.0 - 500 = 400 // according to same id 10 = 10
450.0 - 600 = -150 // 20 = 20
600.0 - 200 = 400 // 30 = 30
]
in this array consider 10,20,30 are ids and next is value i want output where compare ever id and get difference example if (id1 = id2 ){ id1 => value - id2 => value }
i need help in that code which i already tried
$getsellerreport = SellerSellsReport::where('seller_id' , $seller_id);
$getunitdiff = $getsellerreport->pluck('unit')->toArray();// [0 => 75 1 => 500 => 100]
$getamountdiff = $getsellerreport->pluck('amount')->toArray(); // [0 => 11000 => 40 2 => 900]
$getproductdiff = $getsellerreport->pluck('product_id')->toArray(); // [0 => 39 1 => 242 => 23]
foreach($product_report as $preport){
$unit[] = $preport['unit'];// [0 => 75 1 => 25 2 => 100]
$amount[] = $preport['amount'];// [0 => 900 1 => 450 2 => 600]
$product_id[] = $preport['product_id'];// [0 => 23 1 => 242 => 39]
} // here we get array two values
above code get values with starting 0 key value and on below for() loop we can use product_id to compare both product id and get unit and amount but i dont know how i can do that can someone help me?
for ($i = 0 ; $i < sizeof($amount) ; $i++){
$unitdiff[] = $getunitdiff[$i] - $unit[$i];
$amountdiff[] = $getamountdiff[$i] - $amount[$i];
}
You could collect the arrays and use map, here is a sample to get you started:
$a = [
10 => 900.0,
20 => 450.0,
30 => 600.0,
];
$b = [
30 => 200.0,
10 => 500.0,
20 => 600.0,
];
$x = collect($a)->map(function($aItem, $index) use ($b) {
return $aItem - $b[$index];
});
dd($x); // yields [ 10 => 400.0, 20 => -150.0, 30 => 400.0 ]

Calculate values of deep nested multi dimensional arrays in reverse

I need to calculate reward for employees in my company.
I have this structure (for example):
And in PHP it represents the following array:
<?php
$structure = [
"A" => [
"B" => [
"E" => [
"M" => null
],
"F" => [
"N" => [
"T" => null
],
"O" => null
],
"G" => [
"P" => null,
"Q" => [
"U" => null,
"V" => [
"X" => null,
"Y" => [
"3" => [
"4" => [
"4" => null,
"6" => null,
"7" => [
"8" => null
],
]
]
],
"Z" => null
]
]
],
"H" => null
],
"C" => [
"I" => null,
"J" => [
"R" => null
],
"K" => [
"S" => [
"W" => [
"1" => null,
"2" => null,
]
]
]
],
"D" => [
"L" => null
]
]
];
I need to calculate the reward for each employee. The end subordinates have reward only from their own work. But the seniors who have other subordinates have reward from their own work + works their subordinates.
For example:
Person A has own reward 10.
Person D has own reward 20.
Person L has own reward 15.
In the final,
Person L has final reward 15 (is final).
Person D has final reward 20 + 15 = 35 (D + L).
Person A has final reward 10 + 35 (A + D).
The calculation must be carried out below, however, the network can be arbitrarily deep. Calculating I would like to split into several parts. (For performance reasons)
The spider, which revises the structure to the appropriate format.
Calculate reward for each node.
Send information about each node via email.
I do not know how to proceed across the structure. Or to reorganize the structure to undergo easier. Can you think of anything?
I am grateful for you. Thanks!
#Martin
// EDIT: raw database
| id | parent | name
---------------------
| 1 | null | Martin
| 2 | null | Peter
| 3 | 1 | John
| 4 | 3 | Jack
// EDIT: new data structure:
[
"A" => [
"points" => 20,
"childs" => [
"B" => [
"points" => 10,
"childs" => [
"C" => [
"points" => 50,
"childs" => null
]
]
],
"D" => [
"points" => 30,
"childs" => [
"4" => [
"points" => 40,
"childs" => null
]
]
]
]
]
]
This can be done by traversing through the structure recursively from the inside-out, and stores the rewards for each employee it can find in a flattened 2D array.
It is dependent on each leaf of the structure having a starting value, as it needs a base value to calculate back up the tree on.
Using RecursiveIteratorIterator with the CHILD_FIRST flag allows you to loop through the array 'backwards' which is what we want in this case as that is where the starting rewards are. As we go through the tree, we obtain the subordinates rewards, add it to the current employee, and continue .. so by the time we get back to the top of the structure, we have calculated all employees.
Storing the result in a flattened array is then much easier to use and manipulate further along your logic.
Assumed starting structure:
// Each leaf has a value (random for example sake)
$structure = [
"A" => [
"B" => [
"E" => [
"M" => 10
],
"F" => [
"N" => [
"T" => 15
],
"O" => 5
],
"G" => [
"P" => 40,
"Q" => [
"U" => 30,
"V" => [
"X" => 35,
"Y" => [
"3" => [
"4" => [
"5" => 5,
"6" => 10,
"7" => [
"8" => 20
],
]
]
],
"Z" => 30
]
]
],
"H" => 15
],
"C" => [
"I" => 25,
"J" => [
"R" => 25
],
"K" => [
"S" => [
"W" => [
"1" => 40,
"2" => 50,
]
]
]
],
"D" => [
"L" => 15
]
]
];
Calculation:
// Iterate through the structure from the outside-in (child/leaves first)
$data = new RecursiveArrayIterator($structure);
$dataIt = new RecursiveIteratorIterator($data, RecursiveIteratorIterator::CHILD_FIRST);
$rewards = [];
foreach ($dataIt as $value) {
$subKeys = [];
$rewards[$dataIt->key()] ?? $rewards[$dataIt->key()] = 0; // Suppress any undefined index errors
if (is_array($value)) {
$subKeys = array_keys($value);
// traverse through all branches to obtain the existing reward values for subordinates
array_walk_recursive($value, function($reward, $person) use (&$subKeys) {
$subKeys[] = $person;
});
$subKeys = array_unique($subKeys);
foreach ($subKeys as $employee) {
$rewards[$dataIt->key()] += $rewards[$employee];
}
} else {
$rewards[$dataIt->key()] += $value;
}
}
print_r($rewards);
Return/final array:
Array
(
[M] => 10
[E] => 10
[T] => 15
[N] => 15
[O] => 5
[F] => 35
[P] => 40
[U] => 30
[X] => 35
[4] => 60
[6] => 10
[8] => 20
[7] => 20
[3] => 90
[Y] => 180
[Z] => 30
[V] => 335
[Q] => 520
[G] => 745
[H] => 15
[B] => 1060
[I] => 25
[R] => 25
[J] => 25
[1] => 40
[2] => 50
[W] => 90
[S] => 180
[K] => 270
[C] => 435
[L] => 15
[D] => 15
[A] => 1935
)
To get the number of employees under each node, you can do that easily in one line using count:
$employeesPersonA = count($structure['A'], COUNT_RECURSIVE); // 33
$employeesPersonC = count($structure['A']['C'], COUNT_RECURSIVE); // 8
$employeesPersonK = count($structure['A']['C']['K'], COUNT_RECURSIVE); // 4
EDIT:
For your database structure, you cannot get a multi dimensional result set out of the database so your only option there is to go through the result set and build your structure in PHP from that.
Here's one example, simplified to consider only nodes, 'A','B','C',D', & 'L'...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(user CHAR(1) NOT NULL PRIMARY KEY
,lft INT NOT NULL
,rgt INT NOT NULL
);
INSERT INTO my_table VALUES
('A',1,10),
('B',2,3),
('C',4,5),
('D',6,9),
('L',7,8);
SELECT y.user
, GROUP_CONCAT(x.user ORDER BY x.lft) nodes
FROM my_table x
JOIN my_table y
ON x.lft BETWEEN y.lft AND y.rgt
GROUP
BY y.user
ORDER
BY y.lft;
+------+-----------+
| user | nodes |
+------+-----------+
| A | A,B,C,D,L |
| B | B |
| C | C |
| D | D,L |
| L | L |
+------+-----------+

Multidimensional array without repeating

I have two MySQL tables, one is called "version" and the other is called "lines" inside one DB called "project".
The "version" table consist of:
id (PRIMARY / AI) = int
version = string
Rows:
# | version
------------
0 | 100
1 | 200
3 | 400
The "lines" table consist of:
id (PRIMARY / AI) = int
lines = string
version_id = string (ID from table version)
Rows:
# | line | version_id
--------------------------
0 | line #1 | 0
1 | line #2 | 0
2 | line #3 | 1
3 | line #4 | 0
4 | line #5 | 1
How can I create multidimensional array to output an example JSON (pseudo)
"full" =>
"version" => "100"
"id" => "0", (version id table)
"line" =>
"row_0" => "line #1", (from lines table)
"row_1" => "line #2",
"row_2" => "line #4",
"version" => "200"
"id" => "1",
"line" =>
"row_0" => "line #3",
"row_1" => "line #5",
"version" => "300"
"id" => "3",
"line" => "EMPTY" (no lines for this version)
]
I rewrote the code a couple of times but I can't make it work. Either I stuck or I finish in infinite loop of errors. This is what I got for now:
function returnJson() {
$db = DB::query("SELECT * FROM version");
foreach ($db as $row) {
$i++;
$lines = DB::Query("SELECT * FROM lines WHERE version_id=%i", $row['id']);
// approach to nested array?
}
}
I'm using MeekroDB so any approach to MySQL is offset. You can write an example in PDO if you are more familiar with it.
I assume that the array you want would look like this in php:
"full" =>
"100" => array (
"versionId" => "0", (version id table)
"line" =>
"row_0" => "line #1", (
"row_1" => "line #2",
"row_2" => "line #4"
)
, "200" => array (
"versionId" => "1",
"line" => array (
"row_0" => "line #3",
"row_1" => "line #5" )
)
, "300" => array (
"versionId" => "3",
"line" => array()
)
]
Use a JOIN
SELECT v.id AS versionId, v.version l.id as linesId, l.lines
FROM version v
INNER JOIN lines l ON v.id = l.version_id
And then a loop with some if statement to build the array
$versions = array();
foreach($db as $row) {
if (!isset($versions[$db["version"]]))
$versions[$db["version"]] = array (
"versionId" => $db["versionId"],
"line" => array()
);
if (!empty($db["lines"]))
$versions[$db["version"]][lines"][] = $db["lines"];
}
Try the accepted answer in this SO post which also deals with nested JSON data.
Also you may want to reduce your SQL to below and just use one loop instead of 2 nested loops as in the SO post above.
SELECT *
FROM version
INNER
JOIN lines
ON version.id = lines.version_id
Hope this helps.

unable to merge variables with array_merge: undefined index

I am trying to combine two variables that retrieves information from MySQL.
I have been suggested to use array_merge(), and it seems to work for the most part. I keep getting Warning: Illegal string offset after all the results has been returned from the database. Interesting is that the first 8 (the query has a LIMIT of 8) are error clean, after the 8 results has been printed, then a huge list appears with that error.
query
articleClass.php
public function latestArticles()
{
$sth = $this->db->prepare("SELECT * FROM articles
WHERE article_uid = article_uid
ORDER BY article_uid DESC LIMIT 8");
$sth->execute();
$row = $sth->fetchAll();
return $row;
}
public function articleTags()
{
$sth = $this->db->prepare("SELECT a.*, b.*
FROM articles a, article_tags b
WHERE b.tag_id = a.article_uid
");
$sth->execute();
$row = $sth->fetchAll();
return $row;
}
printing code
index.php
include 'libraries/articleClass/articleClass.php';
$articleClass = new articleClass();
$latestArticles = $articleClass->latestArticles();
$articleTags = $articleClass->articleTags();
foreach(array_merge($latestArticles, $articleTags) as $data)
{
$first_uid = $data['article_uid'];
$first_image = $data['article_image'];
$first_title = $data['article_title'];
$first_content = $data['article_content'];
$first_created = gmdate("d M Y", $data['article_created']);
$first_tags = $data['tag_name'];
echo '
<article>
<img src="path-to-image/'.$first_image.'"/>
<h1>'.$first_title.'</h1>
<p>'.$first_content.'</p>
<ul>
<li>'.$first_tags.'</li>
</ul>
</article>
';
}
Once index.php is loaded, 8 articles are printed on the page as they should, but I get :
Notice: Undefined index: tag_name in /var/www/new-design/index.php on line 74
Trial & (mostly) Failures
If I change public function article_tags to Fetch instead of FetchAll I get these errors:
Warning: array_merge(): Argument #2 is not an array in /var/www/new-design/index.php on line 67
Warning: Invalid argument supplied for foreach() in /var/www/new-design/index.php on line 67
I am unable to figure out how to succeed with this, any leads would be great. I've been at it since morning!
UPDATE
article_tags table
+--------------------------------+
| tag_id | article_id | tag_name |
+--------------------------------+
| 1 | 8 | awesome |
| 2 | 8 | sweet |
| 3 | 8 | gross |
+--------------------------------+
there is only one article_id corresponding to the articles that are being called, yet this article still receives undefined index: tag_name of course because in the array_merge they haven't been merged at all.
What happens here is that
array_merge($latestArticles, $articleTags)
simply appends one array to the other, meaning:
The first 8 entries in the resulting array are your articles (these have the "tag_name" field set).
The rest is filled with the contents of $articleTags (those don't have "tag_name" field - causing your error).
Here's some code to illustrate that (Indices 0 to 2 are your articles while 3 to 5 are your tags, notice how indices 3-5 of the resulting array don't have the tag_name field):
$latestArticles = array(
array("article_title" => "Some Title 1", "tag_name" => "SomeTag"),
array("article_title" => "Some Title 2", "tag_name" => "SomeOtherTag"),
array("article_title" => "Some Title 3", "tag_name" => "SomeTag"),
);
$articleTags = array(
array("name" => "SomeTag", "somefield" => "foo", "otherfield" => "bar"),
array("name" => "SomeTagOtherTag", "somefield" => "baz", "otherfield" => "test"),
array("name" => "YetAnotherTag", "somefield" => "test2", "otherfield" => "test3")
);
$result = array_merge($latestArticles, $articleTags);
print_r($result);
/** Resulting Array:
Array
(
[0] => Array
(
[article_title] => Some Title 1
[tag_name] => SomeTag
)
[1] => Array
(
[article_title] => Some Title 2
[tag_name] => SomeOtherTag
)
[2] => Array
(
[article_title] => Some Title 3
[tag_name] => SomeTag
)
[3] => Array
(
[name] => SomeTag
[somefield] => foo
[otherfield] => bar
)
[4] => Array
(
[name] => SomeTagOtherTag
[somefield] => baz
[otherfield] => test
)
[5] => Array
(
[name] => YetAnotherTag
[somefield] => test2
[otherfield] => test3
)
)
*/

Categories