custom order and display foreach PHP - php

Thanks to the many folks who help us out here on Stackoverflow. You all are awesome! Now to the question. I've got an array for the following values: "duck", "chicken","egg", "pork", "steak", "beef", "fish", "shrimp", "deer", and "lamb."
I've gotten the list to display in Alphabetical order. This is a dynamic array, so it may not always have all these values or be in that order. I'd like to have "Steak" always appear first with "Top Choice" next to it, while keeping the rest in alphabetical order with "Available for Order" next to them.
Here's what I've got thus far with $meat_items as the array:
foreach($meat_items as $meat_item)
echo $meat_item . ' Available for Order <br>';
I should clarify: Steak may NOT always be a part of the array.

Since you always want steak to appear first, hard code it:
if (in_array("steak", $meat_items)) {
`echo "Steak: Top Choice";`
}
Sort your array alphabetically:
sort($meat_items);
Then loop through your array, echoing all items except the steak:
foreach ($meat_items as $meat_item) {
if ( "steak" != $meat_item ) {
echo $meat_item . ' Available for Order<br />';
}
}

if (!empty($meat_items['steak']))
{
echo 'Steak Top Choice <br >';
unset($meat_items['steak']);
}
sort($meat_items);
foreach($meat_items as $meat_item)
echo $meat_item . ' Available for Order <br>';

A more general purpose way to do this is to tell PHP how to sort the items, by defining a sorting "comparison" that prefers the "top choices", and then passing it to usort.
I don't really know PHP, but something like:
function prefer_top($a, $b) {
/* We can modify this array to specify whatever the top choices are. */
$top_choices = array('Steak');
/* If one of the two things we're comparing is a top choice and the other isn't,
then it comes first automatically. Otherwise, we sort them alphabetically. */
$a_top = in_array($a, $top_choices);
$b_top = in_array($b, $top_choices);
if ($a_top && !$b_top) { return -1; }
if ($b_top && !$a_top) { return 1; }
if ($a == $b) { return 0; }
return ($a < $b) ? -1 : 1;
}
usort($meat_items, "prefer_top");
// and then output them all in order as before.

Related

PHP code performance optimization help needed

I have the following code, which checks is an element exists, and if it exists, it checks for the same name, with an incremented number at the end.
For example, it checks is the key "test" exists in the array $this->elements, and if it exists, it checks for "test2", and so on, until the key doesn't exist.
My original code is:
if (isset($this->elements[$desired])) {
$inc = 0;
do {
$inc++;
$new_desired = $desired . $inc;
} while (isset($this->elements[$new_desired]));
$desired = $new_desired;
}
I tried with:
if (isset($this->elements[$desired])) {
return $this->generateUniqueElement($desired, $postfix);
}
private function generateUniqueElement($desired, $postfix) {
$new_desired = $desired . $postfix;
return isset($this->elements[$new_desired]) ? $this->generateUniqueElement($desired, ++$postfix) : $new_desired;
}
But in my tests there's no speed improvement.
Any idea how can I improve the code? On all the pages, this code is called over 10 000 times. And sometimes even over 100k times.
Anticipated thanks!
Without further knowledge on how you generate this list, here's an idea:
$highestElementIds = [];
foreach($this->elements as $element) {
preg_match('/(.*?)(\d+)/', $element, $matches);
$text = $matches[1];
$id = (int)$matches[2];
if(!isset($highestElementIds[$text])) {
$highestElementIds[$text] = $id;
} else {
if($id > $highestElementIds[$text]) {
$highestElementIds[$text] = $id;
}
}
}
// find some element by a simple array access
$highestElementIds['test']; // will return 2 in your example
If your code is really being called 100k times, it should be a lot faster to iterate your list only once and then get the highest id directly from an array which contains the highest number (since you don't need to iterate through it again).
That being said, I still wonder what's the actual reason for having such a huge array in the first place...
Typical unique IDs are either random (UUID or random chars) or sequential numbers. The latter is as simple as it gets and it can be generated with a simple counter:
function generateNewElement($postfix) {
static $i = 0;
return sprintf('%d%s', $i++, $postfix);
}
echo generateNewElement('foo'), PHP_EOL;
echo generateNewElement('foo'), PHP_EOL;
echo generateNewElement('foo'), PHP_EOL;
echo generateNewElement('foo'), PHP_EOL;
0foo
1foo
2foo
3foo
Of course this is just a generic solution so it may not fit your specific use case.

MVC multi-dim array displaying in view

I can't think my way through this one. I'm still learning arrays so go easy on me. I'm using codeigniter.
I have 3 tabs (1 month, 2 month, 3 month) in my mark-up.
Each tab shows 3 price boxes (3 levels - basic=1, standard=2, featured=3).
I need to display 9 prices overall, pulled from 1 look-up:
return $this->db->get('prices')->result_array();
In the database it's like this
Should I be trying to do it from one look-up as shown in my model or should I be doing several look-ups, or should I just be managing that look-up in the controller setting vars, ready to display in the view or just doing everything in the view? And How? The only think of 3x foreach loops, where inside the loop I say:
if($prices['months']==3) echo $prices['level'].' is '.$prices['amount'].'<br>';
I'd like to know the BEST way to do this but also how to do the array from one look-up, because I think I really need to get my head around arrays properly. Thanks :)
-- EDIT to show what I've ended up using below --
In the controller, sort of inspired by array_chunk but more manual and to allow for the table to expand, is setting array keys which I read up on in php manual:
foreach ($prices as $price_row) {
$data['prices'][$price_row['months']][] = $price_row;
}
Then in the view I can just use foreach for a month:
foreach ($prices[1] as $p) {
echo level_name($p['level']).' = '.$p['amount'].'<br>';
}
i did not test this so might have made a stupid error - but basically you can foreach through each of your products - make an array - and then use that array in your view.
// in your model Note I am returning an object not an array
// and always check to make sure something got returned
if( ! $products = $this->db->get('prices')->result() )
{
return false:
}
else
{
$prices = array();
foreach($products as $product)
{
// append the months number to the word 'months' to make it clear
$month = $product->months . 'month' ;
// same with level
$level = 'level' . $product->level ;
// build the array
$prices[$month][$level] = $product->amount ;
}//foreach
return $prices ;
}//else
so then in your controller - make sure something came back from the model, assign it to data, then pass data to your view
if( ! $data['prices'] = $this->somemodelname->returnPrices() )
{
$this->showError() ;
}
else
{
$this->load->view('yourviewname', $data);
}
and then in your view you could foreach or just echo out each price if it needs to follow some layout.
echo '1 month level 1 $' . $prices['1month']['level1'] ;
and remember your best friend when doing arrays is print_r wrapped in pre tags so like
echo 'start prices <br> <pre>' ;
print_r($prices) ;
echo '</pre>' ;
opinions - its fine to build stuff in the controller and the view while you are developing and building out. but get in the habit of refactoring to your models. keep your controllers as clean and thin as possible. if your views need complicated data structures - build them in a model first. that way if something goes wrong - your controller can decide what to do. AND you don't have to check in your view if $prices is set and valid because you have already done it. this minimizes where things can go wrong.
Here's a pretty easy way to sort the db return into separate arrays and then display them. #caralot stole my thunder so I came up with this alternative.
Using your current model return $this->db->get('prices')->result_array(); and assigning it to $data.
$data = $this->db->functionName();
//You should check $data validity but I'm skipping that
$month1 = [];
$month2 = [];
$month3 = [];
foreach($data as $row)
{
if($row['months'] === '1')
{
$month1[] = $row;
}
elseif($row['months'] === '2')
{
$month2[] = $row;
}
else
{
$month3[] = $row;
}
}
echo "Month 1<br>";
foreach($month1 as $month){
echo "Level ". $month['level'].' is '.$month['amount'].'<br>';
}
echo "Month 2<br>";
foreach($month2 as $month){
echo "Level ".$month['level'].' is '.$month['amount'].'<br>';
}
echo "Month 3<br>";
foreach($month3 as $month){
echo "Level ".$month['level'].' is '.$month['amount'].'<br>';
}
If your table was less ordered than what you show it would be necessary to add a $this->db->order_by('level', 'ASC'); call to the query.
It is such a tiny dataset that you should definitely do a single lookup. Sorting the array into three arrays in your controller would make more sense as it will leave your view much cleaner, and allow you to set defaults should there be no data (say level 1 for 3 months is removed) if you need to. You are always going to need three foreach loops unless you construct the entire table in one go, setting breaks and new column headings when the level indicator changes.
There is no 'Best' way to do this, it is all case dependent on complexity of layout, future data development and your requirements. Usually though you minimize the number of queries, and keep data manipulation to a minimum within you views. So ideally you will have three arrays, one for each column, sent to your view.
In your controller,
$result = $this->db->query("SELECT * from prices");
$result=$result->result();
$tab1=array();
$tab2=array();
$tab3=array();
foreach ($result as $res) {
switch($res->months)
{
case 1: array_push($tab1, $res);
break;
case 2: array_push($tab2, $res);
break;
case 3: array_push($tab3, $res);
break;
}
}
//var_dump($tab3); //array tab1 for month1, array tab2 for month2, array tab3 for month3
$data['tab1']=$tab1;
$data['tab2']=$tab2;
$data['tab3']=$tab3;
$data['include']=$this->load->view('myview', $data);
In your view i.e myview in my case,
<?php
if(!empty($tab1))
{
echo "Tab 1"."<br>";
foreach($tab1 as $tb)
{
echo "For level ".$tb->level." price is ".$tb->amount."<br>";
}
}
if(!empty($tab2))
{
echo "Tab 2"."<br>";
foreach($tab2 as $tb)
{
echo "For level ".$tb->level." price is ".$tb->amount."<br>";
}
}
if(!empty($tab3))
{
echo "Tab 3"."<br>";
foreach($tab3 as $tb)
{
echo "For level ".$tb->level." price is ".$tb->amount."<br>";
}
}
?>

Sorting objects in array

I have an array of objects containing a number of values. The values are set correctly and the data inside this array is also shown in correct way accoring to the order of objects.
What i would want now ii that the order is changed. In this particular example according to the objects 'Point' value, so the objects with the highest Pointvalue is first and so on...
What I tried to do was finding the object with the highest value, push it in the array and unsetting the original value. And with array_slice getting the relevant array elements in the end.
I also succeeded in the first part, but the problem is I keep finding the same object, so i somehow doesn't remove it from the array.
$max = $obj[0];
for ($j =0; $j<count($obj)-$j; $j++) {
for ($i=0; $i<count($names); $i++) {
if ($max->Point < $obj[$i+1]->Point) {
$max = $obj[$i+1];
}
}
if ($max->id == $obj[$j]->id) {
unset($obj[$j]);
}
array_push($obj, $max);
}
I'm not sure you can see through the code and what I'm trying to do, but hopefully someone can and either show my mistake(s), or show others way to accomplish the same?
Try using the usort() function. It allows you to sort the array according to a comparison function that you define.
function lower_points($a, $b) {
if ($a->Point == $b->Point) return 0;
else if ($a->Point > $b->Point) return -1;
else return 1;
}
usort($array_of_objects, lower_points);

Prepend to array, but keep the indexes with PHP

I have an array that are already sorted.
Now I would like to take all the arrays that have a sub array value 0 and put them at the start of the array.
This is what I tried to do:
foreach($dealStatsArray as $deal_id => $dealStats)
{
if($dealStats['revenueY'] == 0)
{
$tmpArray[$deal_id] = $dealStats; // Store the array
unset($dealStatsArray[$deal_id]); // Unset the current one, since it is not in right position
array_unshift($dealStatsArray, $tmpArray); // Prepend the tmp array, to have it at the beginning of the array
}
}
Now problem is that array_unshift() does:
"All numerical array keys will be modified to start counting from zero" -php net array_unshift()
Which mess up the rest of the code I got because I need to keep the indexes on $dealStatsArray, and the index for the new prepended array should be the $deal_id and not 0.
How can I do this? And I need a solution that can manage to prepend 2 or 3 times to the beginning of the array, just like it works fine with array_push (appending) I would like to do that, but just prepending
Update: Here is my currently uasort function, that are sorting the array after the revenueY value, so that the highest number are in the start of the array and then descending..
function cmp($a, $b)
{
if (($a["revenueY"]) == ($b["revenueY"])) {
return 0;
}
return (($a["revenueY"]) > ($b["revenueY"])) ? -1 : 1;
}
uasort($dealStatsArray, "cmp");
Now if I follow #thaJeztah's answer, which partly works, then I added this under:
function sortbyRevenueY($a, $b) {
if ($a['revenueY'] == $b['revenueY']) {
return 0;
}
return ($a['revenueY'] == 0) ? -1 : 1;
}
uasort($dealStatsArray, 'sortbyRevenueY');
But this does not work correct, It does take all the revenueY==0 arrays and prepend at the beginning of the array, but then the rest of the arrays gets unsorted (highest to lowest, the first uasort())
Here's my final goal: To have an array where all the revenueY==0 are at the beginning of the array, and after these the top revenue are coming after and then descending to lowest revenue at the end of the array.
You can probably achieve this by 'sorting' the array using a custom callback and uasort();
http://www.php.net/manual/en/function.uasort.php
function sortbyRevenueY($a, $b) {
if ($a['revenueY'] == $b['revenueY']) {
return 0;
}
if (0 == $a['revenueY']) {
return -1;
}
if (0 == $b['revenueY']) {
return 1;
}
return (($a["revenueY"]) > ($b["revenueY"])) ? -1 : 1;
}
uasort($dealStatsArray, 'sortbyRevenueY');
print_r($dealStatsArray);
Haven't been able to test it and the 'callback' method may need some tweaking, however, uasort() allows you to sort an array using a custom 'callback' method that determines 'how' the array should be sorted. The values inside the array will be sorted, without losing the key/value relation.
My example is just to illustrate how you can achieve this, but, as mentioned, may need to be tweaked.
[update]
I've updated by example to attempt to combine 'regular' sorting and sorting by '0' into a single callback. Again untested, but maybe this work. Please test this updated example.
You could try merging the arrays instead of unshifting:
$dealStatsArray = array_merge($tmpArray, $dealStatsArray);
So no matter how many items $tmpArray will have, they will be at the beginning

How to sort a table containing csv file data

I have to sort the file data in a table depending on how the user chooses it to be sorted (only options are ascending and descending)
Here is my code for it:
if($submit=="Display"){
if ($headings=="0"){echo "<h2>Error</h2>";}
elseif ($search==""){echo "<h2>Error</h2>";}
else {
if($headings==$headings_array[0])
echo "<table border='1'>";
$f = fopen("data.csv", "r");
while ($line = fgetcsv($f)){
echo "<tr>";
foreach ($line as $cell) {
echo "<td><center>".$cell."</center></td>";
}
echo "<tr>";
}
fclose($f);
function my_sort($a, $b){
if ($a == $b) return 0;
return ($a > $b) ? -1 : 1;
}
$arr = $cell;
usort($arr, "my_sort");
print_r ($arr);
echo "</table>";
}
}
But it's not sorting. Can someone spot what's wrong with it? And if you have a more efficient way of doing this, please let me know as I have 15 headings and it would be a pain to do if statements for each of those headings.
To be clear, here is what I have to do:
For each sort field (which are the headings) that the user chooses, I have to arrange the display according to that heading
i.e if($headings==$headings_array[0])
$headings_array[0] is equal to Names therefore, the table should display the values in alphabetical (or reverse alphabetical) order of Names
Extra info:
$headings is the name of the select box
$headings_array is the array values for the select box
$search is the radio buttons containing whether the table should be sorted as ascending or descending
PHP only
Any help is appreciated!
I think your sort function is wrong. It's doing the same thing as sort() or rsort() would do.
usort simply compares two array elements and determines if they need to be swapped based on the value you return. Your sending in $a and $b, which should be csv lines. Then you compare the indexes that you need to and swap the lines accordingly.
You need to have it sort by the array index given by heading. maybe try something like this:
global $headings;
...
$arrayToBeSorted = array();
// send in an array of lines
while ($line = fgetcsv($f)){
$arrayToBeSorted[] = $line;
}
usort($arrayToBeSorted, 'my_sort');
function my_sort($lineA, $lineB){
// set this to the column that needs to be sorted
global $headings;
$linePartsA = explode(',' $lineA);
$linePartsB = explode(',' $lineB);
if ($linePartsA[$headings] == $linePartsB[$heading) return 0;
return ($linePartsA[$headings] > $linePartsB[$heading]) ? -1 : 1;
}
}
Why do you want to use php only . I used jquery table sorter which is very efficient .here is the link table sorter

Categories