How to edit key/value pairs in multidimensional, associative arrays? - php

I hope my title makes sense, I'm no quite sure how to describe my problem in a shot way, so here is the long version:
I'm working in PHP and I have 2 MySQL tables that are displaying items and their specs (I cannot change the table structure).
T1 (Items)
+---------+--------+
| itemID | item |
+---------+--------+
| 1 | TV |
| 2 | Radio |
| 3 | Camera |
| ... | ... |
+---------+--------+
T2 (Specs)
+---------+--------+------+
| itemID | specID | spec |
+---------+--------+------+
| 1 | color | red |
| 1 | weight | 10 |
| 1 | price | 499 |
| 2 | color | blue |
| ... | ... | ... |
+---------+--------+------+
I want a multidimensional, associative array like this
array(
[0]=> array(
"itemID"=>"1",
"item"=>"TV",
"color"=>"red",
"weight"=>"10",
"price"=>"499")
[1]=> array(
"itemID"=>"2",
"item"=>"Radio",
"color"=>"blue",
...)
)
It seems impossible to me to achieve that with only one query.
I try to query the two tables separately, rearrange the key=>value in T2 and use array_merge.
What I have so far is the array from T1
array(
[0]=> array(
"itemID"=> "1",
"item"=>"TV")
[1]=> array(
"itemID"=> "2",
"item"=>"Radio")
[2]=> array(
"itemID"=> "3",
"item"=>"Camera")
...)
From T2 the array comes in the same form
array(
[0]=> array(
"itemID"=> "1",
"specID"=> "color",
"spec"=>"red")
[1]=> array(
"itemID"=> "1",
"specID"=> "weight",
"spec"=>"10")
[2]=> array(
"itemID"=> "1",
"specID"=> "price",
"spec"=>"499")
...)
After changing the array I will get a single numeric array, not two associative arrays like I want
array(
[0]=> array(
[0] => ["itemID" => "1","color" => "red","weight" => "10", "price" => "499"])
[1]=> array(
[0] => ["itemID" => "2","color" => "blue",...])
...)
My code is
$newArray = array();
for ($i = 0; $i<=n; $i++) {
foreach($oldArray[$i] as $key => $value) {
if ($key === 'itemID') {
$a = $key . '" => "' . $value . '",';
}
elseif($key === 'specID') {
$b = '"' . $key . '" => "';
}
elseif($key === 'spec') {
$c = $value;
}
}
$newArray[] = [$a.$b.$c];
}
Maybe I'm completely wrong here, so any help would be greatly appreciated - Thanks!

This table structure is great if the number of item parameters is variable.
The simplest approach is the following:
$array1 = array(...); // from table 1
$array2 = array(...); // from table 2
$result = array();
foreach ($array1 AS $v) {
$result[ $v['itemID'] ] = $v;
}
foreach ($array2 AS $v) {
$result[ $v['itemID'] ][ $v['specId'] ] = $v['spec'];
}

Consider using group_concat
This will fetch everything you need in 1 query:
select items.itemID,
group_concat(concat(specs.specID,":",specs.spec)) as properties
from items join specs on items.itemID=specs.itemID
group by items.itemID;
Results will look like this:
+--------+-------------------------------+
| itemID | properties |
+--------+-------------------------------+
| 1 | color:red,weight:10,price:499 |
| 2 | color:blue |
+--------+-------------------------------+
I'll leave it to you to populate the array but it should be trivial using explode first with "," delimiter then with ":"

Related

Multi Dimensional Array Construct

I have this type of array that I got from my SQL result.
+-------------+---------------+-----------------+----------------+
| business_id | business_name | business_branch | branch_contact |
+-------------+---------------+-----------------+----------------+
| 1 | ABC | 1 | 1111111111 |
+-------------+---------------+-----------------+----------------+
| 1 | ABC | 2 | 2222222222 |
+-------------+---------------+-----------------+----------------+
| 1 | ABC | 3 | 3333333333 |
+-------------+---------------+-----------------+----------------+
| 1 | ABC | 4 | 4444444444 |
+-------------+---------------+-----------------+----------------+
| 2 | XYZ | 1 | 5555555555 |
+-------------+---------------+-----------------+----------------+
| 2 | XYZ | 2 | 6666666666 |
+-------------+---------------+-----------------+----------------+
At the beginning I thought of doing a separate database query for each branches and businesses. But got to know it was a bad practice.
I want to construct the results as following
[
{
business_id : 1,
business_name : ABC,
branches : [
{
branch_id : 1,
branch_contact : 1111111111
},
{
branch_id : 2,
branch_contact : 2222222222
},
...
},
{
business_id : 1,
business_name : ABC,
branches : [
{
...
}
}
]
}
The current function I'm trying to build doesn't give the necessary output. It is shown below.
$previousBusiness = '';
$previousBranch = '';
$businessAndBranchArray = [];
$businessArray = [];
$branchArray = [];
foreach ($results as $result) {
$currentBranch = [];
$currentBusiness = [];
$currentMerchant = [];
if($result['business_id'] != $previousBusiness && $result['branch_id'] != $previousBranch){
if($previousBranch != '' && $previousBusiness != ''){
$businessArray['business_id'] = $result['business_id'];
$businessArray['business_id'] = $result['business_name'];
$branchArray['branch_id'] = $result['branch_id'];
$branchArray['branch_contact'] = $result['branch_contact'];
} else {
$businessArray['business_id'] = $result['business_id'];
$businessArray['business_id'] = $result['business_name'];
$branchArray['branch_id'] = $result['branch_id'];
$branchArray['branch_contact'] = $result['branch_contact'];
}
$previousBusiness = $result['business_id'];
} else {
$branchArray['branch_id'] = $result['branch_id'];
$branchArray['branch_contact'] = $result['branch_contact'];
}
}
No need to keep track of previous items and such, just use the ID as an array key so you can check it exists. If you don't want it in your final output, use array_values() to remove the keys:
<?php
$results = [
["business_id" => 1, "business_name" => "ABC", "business_branch" => 1, "branch_contact" => "1111111111"],
["business_id" => 1, "business_name" => "ABC", "business_branch" => 2, "branch_contact" => "2222222222"],
["business_id" => 1, "business_name" => "ABC", "business_branch" => 3, "branch_contact" => "3333333333"],
["business_id" => 1, "business_name" => "ABC", "business_branch" => 4, "branch_contact" => "4444444444"],
["business_id" => 2, "business_name" => "XYZ", "business_branch" => 1, "branch_contact" => "5555555555"],
["business_id" => 2, "business_name" => "XYZ", "business_branch" => 2, "branch_contact" => "6666666666"],
];
$output = [];
foreach ($results as $result) {
if (empty($output[$result["business_id"]])) {
$output[$result["business_id"]] = [
"business_id" => $result["business_id"],
"business_name" => $result["business_name"],
"branches" => [],
];
}
$output[$result["business_id"]]["branches"][] = [
"branch_id" => $result["business_branch"],
"branch_contact" => $result["branch_contact"],
];
}
echo json_encode(array_values($output), true);
Output:
[{"business_id":1,"business_name":"ABC","branches":[{"branch_id":1,"branch_contact":"1111111111"},{"branch_id":2,"branch_contact":"2222222222"},{"branch_id":3,"branch_contact":"3333333333"},{"branch_id":4,"branch_contact":"4444444444"}]},{"business_id":2,"business_name":"XYZ","branches":[{"branch_id":1,"branch_contact":"5555555555"},{"branch_id":2,"branch_contact":"6666666666"}]}]

Efficient Database Queries ( PHP + PDO SQL)

At the moment I have a database structure like so:
| id | name | parent_id
| 1 | Human Resources | 0
| 2 | Marketing | 0
| 3 | Operations | 0
| 4 | Design | 0
| 5 | Marketing Design| 4
| 6 | Graphic Design | 4
| 7 | Print Design | 4
| 8 | Human Personal | 1
| 9 | Food Ops | 3
As you can see these are the departments within the business and also sub departments.
A sub-departments parent_id is the id of the department
do, for example:
id: 4, name: Design, parent_id: 0
id: 7, Print Design, parent_id: 4
Print Design is a sub department of design
I have called everything from the database in one query and now I need them in this structure:
$depts = array(
"Human Resources" => array("Human Personal"),
"Marketing" => array(),
"Operations" => array("Food Ops"),
"Design" => array("Marketing Design", "Graphic Design", "Print Design"),
...
);
so far I have:
foreach ($results as $result) {
if ($result['parent_id'] == 0) {
$parentCategories_arr[array($result['id'] => $result['name'])];
} else {
$returnedResults_arr[$result['parent_id']] = array($result['name']);
}
}
However I completely think that I have missed the point. so my question:
How do I loop through all the contents of that results and add the parent categories into an array with their sub categories as an array?
Maybe there is an easier way, but it works : (hate to say that sentence) - try to make it better maybe
$mFinalArray = fixMyArray($results);
print_r($mFinalArray);
function fixMyArray($results){
// Create categories - parent_id == 0
foreach($results as $index => $result) // $index = 0|1|2|3|4|5|6|7|8|9
if($result['parent_id'] == 0) // $result['parent_id'] = current item parent_id
$mCategories[$result['name']] = $result['id']; // $mCategories['Human Resources'] = 1|2|3|4
// Insert each data to the right parent
foreach($results as $index => $result) // $index = 0|1|2|3|4|5|6|7|8
if($result['parent_id'] != 0)
foreach($mCategories as $subindex => $category) // $subindex = Human Resources | Marketing | Operations | Design
if($result['parent_id'] == $category) // $category = 0|1|2|3|4
$mFinalArray[$subindex][] = $result['name']; // ex. $mFinalArray['Human Resources'][] = Human Personal
return $mFinalArray;
}
*Last line has an extra [ ] $mFinalArray[$subindex][ ]= $result['name'] . That means append to array.
Output :
Array
(
[Design] => Array
(
[0] => Marketing Design
[1] => Graphic Design
[2] => Print Design
)
[Human Resources] => Array
(
[0] => Human Personal
)
[Operations] => Array
(
[0] => Food Ops
)
)

PHP get distinct ids count from an array by date

Looking for the simplest way to achieve the following in php:
I have this array with dates and ids
$data = array(
array("date" => "2016-01", "ids" => array(1,2,3,4,5,6,7,8)),
array("date" => "2016-02", "ids" => array(1,2,9,10,11,12)),
array("date" => "2016-03", "ids" => array(3,16,17,18,19,20,21)),
array("date" => "2016-04", "ids" => array(1,2,3,19,20,22,23))
);
The idea is to count ids by date but also return count(existing ids) since the start of every month separately. (I hope this is understandable)
The returned array should look like this:
$data = array(
array("date" => "2016-01", "counter" => array(8)),
array("date" => "2016-02", "counter" => array(2,4)), /* 2 existing ids from 2016-01 and 4 new in 2016-02) */
array("date" => "2016-03", "counter" => array(1,0,6)), /* 1 existing from 2016-01 and 0 exiting from 2016-02 and 6 new */
array("date" => "2016-04", "counter" => array(3,0,2,2)) /* etc. */
);
| 2016-01 | 2016-02 | 2016-03 | 2016-04
------ | ------- | ------- | ------- | -------
2016-01 | 8 | | |
------ | ------- | ------- | ------- | -------
2016-02 | 2 | 4 | |
------ | ------- | ------- | ------- | -------
2016-03 | 1 | 0 | 6 |
------ | ------- | ------- | ------- | -------
2016-04 | 3 | 0 | 2 | 2
Of course if there is a way to do that directly in sql i'll take it :)
Here is one way to do it. (I'm not sure if it's the simplest). Looping over the set of data is the easiest part.
Within that, consider each id from the current day with while ($id = array_pop($day['ids'])). For each of those ids, start at the first day, and look for the id in that days set of ids. If it's found, increment the count for that day and continue with the next id (continue 2 continues the while loop.) If it's not found, move on to the next day. If it's not found when you get to the current day (indicated by $i < $key in the for loop) then increment the count for the current day and move on to the next id.
foreach ($data as $key => $day) {
$counts = array_fill(0, $key + 1, 0);
while ($id = array_pop($day['ids'])) {
for ($i=0; $i < $key; $i++) {
$past_day = $data[$i];
if (in_array($id, $past_day['ids'])) {
$counts[$i]++;
continue 2;
}
}
$counts[$key]++;
}
$new_data[] = ['date' => $day['date'], 'counter' => $counts];
}
p.s. I have no idea how to do this in SQL.
You can do something like this to achieve the desired result,
$newArray = array();
$count = count($data);
for($i = 0; $i < $count; ++$i){
$newArray[$i] = array('date' => $data[$i]['date'], 'counter' => array());
for($j = 0; $j < $i; ++$j){
$counter = 0;
foreach($data[$i]['ids'] as $key => $id){
if(in_array($id, $data[$j]['ids'])){
++$counter;
unset($data[$i]['ids'][$key]);
}
}
$newArray[$i]['counter'][] = $counter;
}
$newArray[$i]['counter'][] = count($data[$i]['ids']);
}
// display $newArray array
var_dump($newArray);
Here's the live demo

PHP Comment System using Laravel

I am developing a website using laravel PHP and trying to do a comment system using the following structure:
- Comment 1 (id = 1)
-- Reply 1 (id = 2) (parent_id = 1)
--- Reply 2.1 (id = 3) (parent_id = 2)
-- Reply 2 (id = 4) (parent_id = 1)
I am wondering how would I do a foreach to cover that? Since i don't know how many child comments a comment will have.
I wouldn’t store comments and replies in a separate table as they’re both comment entities at the end of the day. Simply have a parent_id column in your comments table, and you can fetch both comments and replies in one database query as opposed to two.
I assume you also have a foreign key linking a comment to something like a post. You can then fetch all comments for that post ID:
$comments = Comment::latest()->where('post_id', '=', $post->id)->get();
Then sort them based on their parent_id value:
$comments = $comments->keyBy('parent_id');
You can then iterate over them in your Blade template like this and every iterate, check if there are comments with that comment’s ID as its parent ID:
<!-- Kick-start the loop -->
#foreach($comments[0] as $comment)
#include('partials.comment')
#endforeach
The content of partials/comment.blade.php
<blockquote class="comment">
<p class="comment-body">{{ $comment->body }}</p>
<footer>
<span class="comment-author">{{ $comment->user->name }}</span>,
<time class="comment-date" pubdate="pubdate">{{ $comment->created_at }}</time>
</footer>
</blockquote>
#if(isset($comments[$comment['id']])
#each('partials.comment', $comments[$comment['id'], 'comment')
#endif
Table Like:
+------------+-----------+---------+
| comment_id | parent_id | comment |
+------------+-----------+---------+
| 1 | 0 | test |
| 2 | 1 | test1 |
| 3 | 0 | test2 |
| 4 | 0 | test3 |
| 5 | 1 | test4 |
| 6 | 2 | test4 |
| 7 | 4 | test5 |
| 8 | 5 | test6 |
| 9 | 6 | test7 |
| 10 | 4 | test8 |
| 11 | 3 | test9 |
+------------+-----------+---------+
Get first level parent:
$comments = Comment::where('parent_id', '0')->orderBy('comment_id', 'asc')->get();
$result = array();
foreach($comments as $comment){
$list = array();
$list = array_merge($list, [['comment_id' => $comment->comment_id, 'parent_id' => $comment->parent_id, 'comment' => $comment->comment]]);
$result = array_merge($result, $this->get_child_comment($comment->comment_id,0, $list));
}
function get_child_comment($pid,$level,$list=array()) {
$sub_comments = Comment::where('parent_id','=',$pid)->where('comment_id','!=',$pid)->orderBy('comment_id', 'asc')->get();
foreach($sub_comments as $sub_comment){
$space=" "; sigm='-';
for($j=0; $j<=$level; $j++)
{
$space .=$space;
}
for($j=0; $j<=$level; $j++)
{
$space .= $sigm;
}
$sub_comment->comment = html_entity_decode($space, ENT_QUOTES, "utf-8").' '.$sub_comment->comment;
$list = array_merge($list, array(['comment_id' => $sub_comment->comment_id, 'parent_id' => $sub_comment->parent_id, 'comment' => $sub_comment->comment]));
$list = $this->get_child_comment($sub_comment->comment_id, $level+1, $list);
}
return $list;
}
}
return get array.simple print using foreach:
foreach($result as $val) {
echo $val['comment'].'<br>';
}
Output:
test
- test1
-- test4
--- test7
- test4
-- test6
test2
- test9
test3
- test5
- test8
You can represent comments in following structure:
$comments = [
(object)[
'id' => 1,
'text' => 'Comment 1',
'children' => [
(object)[
'id' => 2,
'text' => 'Reply 1',
'children' => [
(object)[
'id' => 2,
'text' => 'Reply 1.1'
]
]
],
(object)[
'id' => 4,
'text' => 'Reply 2',
]
]
]
];
And print them using recursive function like this:
function printComments($comments, $prefix = '-') {
foreach ($comments as $comment) {
echo $prefix.$comment->text.'<br>';
isset($comment->children) && printComments($comment->children, $prefix.'-');
}
}
Or calling view recursively in case of Laravel:
#include('comments.view.path') inside of
#include('comments.view.path')
For convinient retrieving of comments in the represented structure and generally for working with tree structure I suggest using nested set model and etrepat/baum for Laravel which has toHierarchy() method.

Database array only returns one row

I am sorry for my lazy title. I hope that a moderator could improve it so the database won't get infected.
I got the following code (forum.php);
<?php
$res = $db->query('
SELECT *
FROM forums_categories
ORDER BY category_id
');
while ($row = $db->fetch_array($res)) {
$categories = array(
'ID' => $row['category_id'],
'NAME' => $row['category_name']
);
echo '<pre>';
print_r($categories);
echo '</pre>';
}
And I got the following database structure;
|---------------|-------------------|
| category_id | category_name |
|---------------|-------------------|
| 1 | Example 1 |
| 2 | Example 2 |
| 3 | Example 3 |
| 4 | Example 4 |
| 5 | Example 5 |
| 6 | Example 6 |
|---------------|-------------------|
But my array only returns 1 value:
Array
(
[ID] => 1
[NAME] => Example 1
)
Oh and if somebody likes to know how my $db->fetch_array looks like:
<?php
function fetch_array($result)
{
return mysql_fetch_assoc($result);
}
How can I return all rows in my array? Thank you for reading and thank you for replying!
You're overwriting the previous value of $categories on each iteration
$categories[] = array(
'ID' => $row['category_id'],
'NAME' => $row['category_name']
);
You might also want to initialize an empty array
$categories = array();
before your loop to avoid warnings.

Categories