PHP Help Building Multi-Dimensional Array in For Loop - php

I am newish to PHP and web development. I am struggling a bit on how to best program this multi-dimensional array in PHP using for loops.
Essentially, it is a review website. I am using Bootstrap to render the data in different tabs. The data is being called via a jquery ajax request and the PHP page is returning JSON. Handlebars is being used to render the data in a template.
In the end, I'd like to send the data in roughly this format:
{
review: {
reviewstotal: INT,
count: INT,
summary: {
//iterated
tab: INT,
[{
Question:,
Value:,
Color: ,
Percent:,
},
{
Question:,
Value:,
Color: ,
Percent:,
}]
}
}
}
Where I'm struggling is at the "summary" section. The "Questions" are in an array, and I have the "Values" as another array. The color value would come from an if statement checking the "Value" and set a css value in the template. The percent is the "Value" multiplied by 10. Here is what I have so far to generate the array.
$array = array();
$color ="";
for ($x = 0; $x <= 5; $x++) {
$summary= [];
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
$summary = array_push ($array, $aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10);
}
What I get as output is:
"summary": ["The facilities of the school adequately provide for a positive learning/working experience.",null,"danger",0,
"School provides adequate access to teaching materials without any extra expense by the teacher (books, lab supplies, art supplies, materials, copying, etc.)","9.50","danger",95,
"School is well funded to provide students and faculty with adaquate materials to support their course offering.","9.50","danger",95,
"Parent community is actively involved in supporting the school's mission and their child's education endeavors.","9.00","danger",90,
"Classroom student to teacher ratios are kept low.","8.75","danger",87.5,null,"7.63","danger",76.3]
What I am trying to achieve though is that each "Question" section is wrapped in it's own array. Like:
"summary": [["The facilities of the school adequately provide for a positive learning/working experience.",null,"danger",0],
["School provides adequate access to teaching materials without any extra expense by the teacher (books, lab supplies, art supplies, materials, copying, etc.)","9.50","danger",95],
["School is well funded to provide students and faculty with adequate materials to support their course offering.","9.50","danger",95],
["Parent community is actively involved in supporting the school's mission and their child's education endeavors.","9.00","danger",90],
["Classroom student to teacher ratios are kept low.","8.75","danger",87.5,null,"7.63","danger",76.3]]
And then I can add a consistent key to each of them.

You shall try the following -
$array = array();
$color ="";
//taking this out from the scope of loop
$summary= [];
for ($x = 0; $x <= 5; $x++) {
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
//this is where the diference lies. $summary is a multi-dimension array.
//which means, each element of that array is an array itself.
//So this is how we represent such stuff in php.
//Element at index $x, is also defined as an array with keys containing the
//required values.
$summary[$x] = array(
"text" => $aquest[$x],
"somekey1" => ${"q{$x}"},
"somekey2" => $color,
"somekey3" => ${"q{$x}"}*10
);
}
//here you can print the summary.
print_r( $summary)

You're wanting to add arrays to an array, but currently you're adding variables to an array. So, you need to wrap $aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10 in an array.
$array = array();
$color ="";
for ($x = 0; $x <= 5; $x++) {
$summary= [];
if ( ${"q{$x}"} <= 40){
$color = "danger";
} else if ( ${"q{$x}"} >= 70){
$color = "success";
} else {
$color = "warning";
}
$summary = array_push ($array, array($aquest[$x], ${"q{$x}"}, $color, ${"q{$x}"}*10));
}

Related

PHP arrays - unique combinations out of 3 arrays

I'm not even sure how to approach the problem so I'm just stating the problem. Any help is highly appreciated.
There's this array ($colors) of all possible values:
$colors = array ('red','blue','green','yellow');
Then there's an array ($boxes) of all the possible values - consisting of equal number of values as $colors:
$boxes = array ('circular','squared','hexagonal','triangular');
There's this third array which defines the constraints in making the unique combination:
$possible_combos = array
(
array('circular','red','blue'),
array('squared','red','green'),
array('hexagonal','blue','yellow'),
array('triangular','red','green')
);
Question: How do I get a new array $result with key=>value combinations just that each box is assigned a unique color out of it's possible sets of colors
So a valid $result array would be:
Array ( [circular] => red
[squared] => blue
[hexagonal]=> yellow
[triangular] => green
)
NOTE: If you traverse sequentially, 'red', 'blue', 'green' might get assigned to first three 'boxes' and there might not be anything to pick for the fourth box (since 'yellow' is not allowed to be assigned to it.
I'm thinking, to process the least occurring 'colors' first but really unsure how to handle it syntactically.
Once again thanks for looking into it, in advance! Even some help with how to approach the problem would be nice.
Following code is not producing the correct output either:
foreach ($colors as $c => $color) {
foreach ($boxes as $b => $box) {
for ($i=0; $i < count($colors) ; $i++) {
if(in_array($possible_combos[$i][1],$colors) && !in_array($possible_combos[$i][1], $result))
{
$result[$box] = $possible_combos[$i][1];
unset($colors[$c]);
break;
}
else if(in_array($possible_combos[$i][2],$colors) && !in_array($possible_combos[$i][2], $result))
{
$result[$box] = $possible_combos[$i][2];
unset($colors[$c]);
break;
}
}
}
}
I'm thinking, to process the least occurring 'colors' first but really unsure how to handle it syntactically.
That's a good intuition considering the fact that you have only one box type that can share one of many colors. Because your constraining resource is color, I think it makes sense to sort your rules by them first and then you can distribute by scarcity.
https://3v4l.org/u5pBK
$colors = array ('red','blue','green','yellow');
$boxes = array ('circular','squared','hexagonal','triangular');
$possible_combos = array
(
array('circular','red','blue'),
array('squared','red','green'),
array('hexagonal','blue','yellow'),
array('triangular','red','green')
);
// collect constraints ordered by rarest
foreach ($possible_combos as $constraint) {
$box = array_shift($constraint);
foreach ($constraint as $color) {
$constraints[$color] []= $box;
}
}
// assign rarest first to last
asort($constraints);
foreach ($constraints as $color => $allowedBoxes) {
foreach ($allowedBoxes as $box) {
$key = array_search($box, $boxes);
// if we have a match, then remove it from the collection
if ($key !== false) {
$result[$box] = $color;
unset($boxes[$key]);
continue 2;
}
}
}
print_r($result);
Array
(
[hexagonal] => yellow
[circular] => blue
[squared] => green
[triangular] => red
)

Comparing two arrays of objects and replacing values

I have 2 arrays of objects in a e-commerce project (built in Code Igniter), one an array of product objects and one an array of sale item objects, with product id's and the amount of discount. I need to compare the arrays so I can put a new price in the items which are on sale, but I cannot seem to code the loops properly. Here is my current code:
private function checkSalesProducts($query) { //$query is an array of product objects
$this->db->select("sale_product, sale_discount");
$salesItems = $this->db->get("sale_items");
foreach($salesItems->result() as $salesItem)
{
for($i=0; sizeof($query); ++$i)
{
if($salesItem->sales_product == $query[$i]->prod_id)
{
$query[$i]->prod_price = $query[$i]->prod_price * (1 - $salesItem->sales_discount);
$query[$i]->sale_item = true;
break;
}
}
}
echo "<pre>";
print_r($query);
echo "</pre>";
}
Any ideas?
Your for() should be
for($i = 0; $i < sizeof($query); $i++)
Your script should freeze at the moment since you tell it to loop while sizeof($query) > 0, which it always have since you don't modify it in the loop.

Algorithm to select 1 of 2 Items Based on Probability

I'm coding a project that generates two arrays containing data. One array contains data for a specific country and the other contains data for all countries.
For example, if a user from the US makes a request, we will generate two arrays with data. One with data only for the US and the other with data for worldwide, including the US. I want to give the US array a 60% chance of being selected if the visitor is from the US. That means the other array will have a 40% chance of being selected.
How does one code this??
if(rand(1, 100) <= $probability_for_first_array)
{
use_the($first_array);
}
else
{
use_the($second_array);
}
I find this a straightforward, easy to read solution
<?php
$us_data = "us";
$worldwide_data = "worldwide";
$probabilities = array($us_data => 0.60, $worldwide_data => 0.40);
/* Code courtesy of Jesse Farmer
* For more details see http://goo.gl/fzq5
*/
function get_data($prob)
{
$random = mt_rand(0, 1000);
$offset = 0;
foreach ($prob as $key => $probability)
{
$offset += $probability * 1000;
if ($random <= $offset)
{
return $key;
}
}
}
?>
Gabi's example is fine for two sets, but if you have more data sets to pick from, the if-else structure is not appropriate.

I made a horrible loop.... help fix my logic please

I know I'm doing this a bad way... but I'm having trouble seeing any alternatives. I have an array of products that I need to select 4 of randomly. $rawUpsellList is an array of all of the possible upsells based off of the items in their cart. Each value is a product object. I know this is horribly ugly code but I don't see an alternative now.... someone please put me out of my misery so this code doesn't make it to production.....
$rawUpsellList = array();
foreach ($tru->global->cart->getItemList() as $item) {
$product = $item->getProduct();
$rawUpsellList = array_merge($rawUpsellList, $product->getUpsellList());
}
$upsellCount = count($rawUpsellList);
$showItems = 4;
if ($upsellCount < $showItems) {
$showItems = $upsellCount;
}
$maxLoop = 20;
$upsellList = array();
for ($x = 0; $x <= $showItems; $x++) {
$key = rand(0, $upsellCount);
if (!array_key_exists($key, $upsellList) && is_object($rawUpsellList[$key])) {
$upsellList[$key] = $rawUpsellList[$key];
$x++;
}
if ($x == $maxLoop) {
break;
}
}
Posting this code was highly embarassing...
Actually, pulling randomly from an array is a tough nut to crack - even Microsoft had trouble recently. That's a decent code sample for someone who I assume isn't an expert in algorithms, but also may be statistically skewed. As I said, it's difficult to do this right.
Thankfully, PHP already has the function array_rand, which seems to do what you want: return N items randomly chosen from an array. Is that what you're looking for?
$upsellList = array_rand($rawUpsellList, 4);
I am not really into PHP, but as an algorithm I will consider this pseudocode or whatever:
List<WhateverTypeYouWant> array;
List<WhateverTypeYouWant> selectedElements;
for (int i = 1; i <= 4; i++)
{
int randomIndex = random(array.size());
selectedElements.add(array[randomIndex]);
array.remove(randomIndex);
}
array_rand will let you pick one or more elements randomly from an array.
To use it (and save yourself a lot of headache), simply do something like
$upsellList = array_rand($rawUpsellList, 4);

Keeping top 5 values in PHP efficently

I'm writing a small algorithm in PHP that goes through n number of movies with ratings, and will store the top 5. I'm not reading from a datafile, but from a stream so I cannot simply order the movies by rating.
My question is what is the most efficent way to keep track of the top 5 rated movies as I read the stream? Currently I do the following:
Read in 5 movies (into an array called movies[]), with two keys movies[][name] and movies[][rating]
Order the array by movies[rating] using array_multisort() (highest rating now sits at movies[4])
Read in the next movie
If this new movie rating > movies[0][rating] then replace movies[0] with this new movie
Re-order the list
Repeat 3-5 until finished
My method works, but requires a sort on the list after every read. I believe this to be an expensive method mostly due to the fact that every time I use array_multisort() I must do a for loop on 5 movies just to build the index to sort on. Can anyone suggest a better way to approach this?
Linked lists would work here.
Build a linked list that chains the first 5 movies in the correct order. For each new movie, just start at the the end of the chain and walk it until your movie is between one with a higher rating and one with a lower rating. Then insert your link into the list here. If the movie was better than the worst (and thus your list is now 6 long), just remove the last link in the chain, and you are back to 5.
No sorting, no indexing.
Your algorithm looks fine. I am not sure how the arrays are implemented in PHP. From an algorithm point of view: use a heap instead of an array.
No point in re-sorting after every read since you really only need to insert a new entry. Use the following algorithm, it's likely to get you the best speed. It's basically an unrolled loop, not the most beautiful code.
set movies[0..4].rating to -1.
while more movies in stream:
read in next movie.
if movie.rating < movies[0].rating:
next while
if movie.rating < movies[1].rating:
movies[0] = movie
next while
if movie.rating < movies[2].rating:
movies[0] = movies[1]
movies[1] = movie
next while
if movie.rating < movies[3].rating:
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movie
next while
if movie.rating < movies[4].rating:
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movies[3]
movies[3] = movie
next while
movies[0] = movies[1]
movies[1] = movies[2]
movies[2] = movies[3]
movies[3] = movies[4]
movies[4] = movie
At the end, you have your sorted list of movies. If there's less than 5, those others will have a rating of -1 so you'll know they're invalid. This is assuming that the rating on a real movie is zero or greater but you can adjust the values if they're not.
If you need to adjust it for more than 5 movies, you can. The best bet would be to roll up the loop again. At some point, however, it's going to become more efficient to sort it than use this method. This method's only really good for a small data set.
My method works, but requires a sort on the list after every read.
No it doesn't, it only requires a sort after you find a new movie whos rating is > movies[0][rating].
This method seems efficient to me. You only sort occasionally when there's a new entry for the top 5, which will happen less the more movies you process.
How big is the list? I'm guessing it's not an option to keep the entire list in memory, and sort it at the end?
there is no need for two keys in array. array with name as key, and rating as value will do. Sort it with arsort();
the algorithm is not perfect, you can do it optimally with linked list. Although I think linked list implemented in PHP will be actually slower that function call to asort() for 6 elements. For big O estimation, you can assume that sorting 6 elements has constant time.
You'll only sort when you encounter movie rated higher then the actual, so in average case you'll do it less an less often, while progressing. You'll sort on every movie only in worst case scenario of having initial list sorted from lowest rated.
Here’s what I would do:
// let’s say get_next_movie () returns array with 'rating' and 'name' keys
while ($m = get_next_movie ()) {
$ratings[$m['rating']][] = $m['movie'];
$temp_ratings = $ratings;
$top5 = array ();
$rating = 5;
while (1) {
if (count ($temp_ratings[$rating])) {
$top5[] = array_shift ($temp_ratings[$rating]);
} elseif ($rating > 0) {
--$rating;
} else {
break;
}
}
// $top5 has current top 5 :-)
}
$ratings array looks like this, each rating has array of movies inside:
Array
(
[5] => Array
(
[0] => Five!
)
[3] => Array
(
[0] => Three
[1] => Threeeeee
[2] => Thr-eee-eee
)
[4] => Array
(
[0] => FOR
)
)
Maybe this can be of help.
class TopList {
private $items = array();
private $indexes = array();
private $count = 0;
private $total = 5;
private $lowest;
private $sorted = false;
public function __construct($total = null) {
if (is_int($total))
$this->total = $total;
$this->lowest = -1 * (PHP_INT_MAX - 1);
}
public function addItem($index, $item) {
if ($index <= $this->lowest)
return;
$setLowest = $this->count === $this->total;
if ($setLowest) {
/* //remove first added
$lowestIndex = array_search($this->lowest, $this->indexes);
/*/ //remove last added
$lowestIndex = end(array_keys($this->indexes, $this->lowest));
//*/
unset($this->indexes[$lowestIndex], $this->items[$lowestIndex]);
} else {
++$this->count;
$setLowest = $this->count === $this->total;
}
$this->indexes[] = $index;
$this->items[] = $item;
$this->sorted = false;
if ($setLowest)
$this->lowest = min($this->indexes);
}
public function getItems() {
if (!$this->sorted) {
array_multisort($this->indexes, SORT_DESC, $this->items);
$this->sorted = true;
}
return $this->items;
}
}
$top5 = new TopList(5);
foreach ($movies as $movie) {
$top5->addItem($movie['rating'], $movie);
}
var_dump($top5->getItems());

Categories