For the sake of simplicity let's say I have the following array:
$a = array();
$a[0]['lab'] = 1;
$a[0]['name'] = 'test1';
$a[1]['lab'] = 1;
$a[1]['name'] = 'test2';
$a[2]['lab'] = 2;
$a[2]['name'] = 'test3';
$a[3]['lab'] = 2;
$a[3]['name'] = "test4";
$a[4]['lab'] = 2;
$a[4]['name'] = "test5";
Keep in mind that the length of that array is completely variable and the number of items associated with each lab can vary as well. There might be one per lab there might be 100.
My desired HTML structure is the following:
<div class="parent"> <!-- Records for lab == 1 -->
<div class="child">test1</div>
<div class="child">test2</div>
</div>
<div class="parent"> <!-- Records for lab == 2 -->
<div class="child">test3</div>
<div class="child">test4</div>
<div class="child">test5</div>
</div>
I currently have a for loop with a bunch of extra logic now which is ugly/inefficient and occasionally misses the closing tag for the last "parent" div. I know there is a far more elegant way to do this and I would love to see what others have come up with.
** Edit:
Here is the logic I have in place now which is actually working for the test cases I have thrown at it but it looks horrible:
<?php
$labId = 0;
for($i = 0; $i < count(a); $i++)
{
if(($i+1) < count($a)) { $j = $i + 1;}
if($labId == 0 || $labId != $a[$i]['lab'])
{
echo '<div class="parent">';
}
echo '<div class="child">'.$a['name'].'</div>';
if(($a[$j]['lab'] != $labId && $a[$i]['lab'] != $labId && $labId != 0) || count($a) == 1)
{
echo '</div>';
}
$labId = $a[$i]['lab'];
}
?>
I would do it like this:
$a = ... // copied from your code
$newArray = array();
foreach($a as $e){
$newArray[$e['lab']][] = $e['name'];
}
foreach($newArray as $lab){
echo "<div class=\"parent\">\n";
foreach($lab as $child){
echo "\t<div class=\"child\">".$child."</div>\n";
}
echo "</div>\n";
}
Hope that's simple enough.
The code below should do the trick without creating a new array
foreach ($a as $key0 => $labs) {
foreach ($labs as $key1 => $value) {
if ($key1 == 'lab') {
echo '<div class="parent">';
} else {
echo '<div class="child">'.$value.'</div>';
}
}
//Close parent divs
if (isset($key0['lab'])) {
echo '</div>';
}
}
My solution was somewhat of a combination of the responses and some other trial and error.
<?php
// define original test array
$a = array();
$a[0]['lab'] = 1;
$a[0]['name'] = 'test1';
$a[1]['lab'] = 1;
$a[1]['name'] = 'test2';
$a[2]['lab'] = 2;
$a[2]['name'] = 'test3';
$a[3]['lab'] = 2;
$a[3]['name'] = "test4";
$a[4]['lab'] = 2;
$a[4]['name'] = "test5";
// new array has to be defined outside the foreach() loop, or it will be treated as a local var
$anew = array();
// for each entry, assign to $anew[lab#][next_avail_entry#]['name'] = <value>
foreach ($a as $arec)
$anew[$arec['lab']][]['name'] = $arec['name'];
// output: one parent div for each lab, plus subdivs for as many values as are avail for each lab
foreach ($anew as $alab)
{
printf("<div class=\"parent\">\n");
foreach ($alab as $arec)
printf(" <div class=\"child\">%s</div>\n", $arec['name']);
printf("</div>\n");
}
?>
* Edit: One other solution I found was to query for the labs and items separately and loop through the lab as the outer loop and the items as an inner foreach loop.
Related
I have 11 news then i want to display like
first column need only one news
second column need 5 news
third column need the rest of 5 news
I tried this but no luck, it shows first column and second column and the rest outside column
$rows = array(
'Title1',
'Title2',
'Title3',
'Title4',
'Title5',
'Title6',
'Title7',
'Title8',
'Title9',
'Title10',
'Title11',
);
$total_rows = count($rows);
$total_cols = $total_rows - 1;// remove first one for the first column
$left_column = ceil($total_cols / 2);
$right_column = $total_cols - $left_column;
$i = 0;
echo "<div class='row'>";
foreach ($rows as $row) {
$i++;
if ($i == 1) {
$class = "primary_post";
echo "<div class='col-md-4 main'>";
} elseif ($i <= $left_column) {
$class = "other_post";
echo "<div class='col-md-4 left'>";
} elseif ($i == $right_column) {
$class = "other_post";
echo "<div class='col-md-4 right'>";
} else {
$class = "other_post";
}
echo "<div class='card {$class}'>$i</div>";
if ($i == 1 || $i == $left_column || $i == $right_column) {
echo "</div>";
} else {
echo "";
}
}
echo "</div>";
The last echo "</div>"; supposed to be in the FOR loop not outside of it.
Also, you can split the array into chunks;
$firstColumn = array_splice($rows,0,1);
$secondColumn = array_splice($rows,0,5);
Rest are in the $rows variable.
So instead of dealing with ifs in for loops, you can use 3 different loops and list items in desired HTML objects.
Suppose I have 10 records, and 2 different classes namely c1, c2
<div class="c2">..content..</div>
<div class="c2">..content..</div>
<div class="c1">..content..</div>
<div class="c1">..content..</div>
<div class="c2">..content..</div>
<div class="c2">..content..</div>
<div class="c1">..content..</div>
<div class="c1">..content..</div>
<div class="c2">..content..</div>
<div class="c2">..content..</div>
i want the if else condition where class order needs to maintained like c2,c2,c1,c1,c2,c2,c1,c1,c2,c2.....so on
foreach(x as y){
if() {
<div class="<?php echo $class1;?>">..content..</div>
} else {
<div class="<?php echo $class2;?>">..content..</div>
}
class order should be c2,c2,c1,c1,c2,c2,c1,c1,c2,c2.....so on
Check the page url http://themesflat.com/html/nah/portfolio-creative.html
You can use a bitwise operator (&) here, and with 2 and this will toggle the class...
$i = 0;
foreach($x as $y){
if($i & 2) {
$class = "1";
} else {
$class = "2";
}
echo "<div class=$class>..content..</div>";
$i++;
}
I've extracted the echo out as this keeps it consistent, just have a variable for the class you want to have and put that in the if instead.
You can do it with a separate counter
$current = 'c2';
$count = 1;
for ($i = 0; $i < 10; $i++) {
echo '<div class="'. $current . '">..content..</div>';
if ($count == 2) {
$current = $current == 'c1' ? 'c2' : 'c1';
$count = 0;
}
$count++;
}
Demo: https://3v4l.org/7ftjg
This will of course work just as well in a foreach-loop.
Note: If it's only about styling the divs differently, you could do this in CSS alone. Here's a similar question and answer: nth-child: how to pick elements in groups of two
I'm pretty sure you can resolve the problem via css pseudo class :nth-of-type but if you prefered PHP you can try the following example.
<?php
$class_types = ['c2', 'c1'];
$records = array_fill(0, 10, '..content..'); // replace with your records
$records_length = count($records);
$html_output = '';
foreach ($records as $index => $item) {
$type = floor($index / $records_length * $records_length / 2) % 2;
$class = $class_types[$type];
$html_output .= sprintf('<div class="%s">%s</div>', $class, $item);
}
echo $html_output;
Try this code:
$counter = 1;
for($i=0;$i<=10; $i++){
if($counter==1 or $counter==2) {
echo '<div class="c1">..content..</div>';
} else {
echo '<div class="c2">..content..</div>';
}
if($counter==4){
$counter=0;
}
$counter++;
}
?>
How about we chunk the main array into an array or arrays of two
like
<?php
$chunked_array = array_chunk( $your_array , 2 );
// This will give us an array of 2 elements array
// now we will loop
for( $i = 1 ; $i < count( $chunked_array ); $++ ){
$contents = $chunked_array[$i];
foreach( $contents as $content ){
if ( $i%2 == 0 ){
//<div class="<?php echo $class1;?>">..content..</div>
}else{
//<div class="<?php echo $class2;?>">..content..</div>
}
}
}
using this way we have one loop and it will always be in a group of two even when your data grow or shrink
When I launch my web page, increment doesn't work correctly!
It should go like this: $i = from 1 to x (0,1,2,3,4,5,6 etc..).
But instead it jumps over every step giving result of (1,3,5,7 etc..).
Why is this code doing this?
<ul class="about">
<?php
$result = mysql_query("SELECT * FROM info WHERE id = 1");
while ($row = mysql_fetch_assoc($result))
{
$bioText = $row['bio'];
}
$endBioTxt = explode("\n", $bioText);
for ($i=0; $i < count($endBioTxt);)
{
if (checkNum($i) == true)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
$i++;
}
// Function to check if number is prime
function checkNum($num){
return ($num % 2) ? TRUE : FALSE;
}
?>
</ul>
Output:
Sometext!(right side)
0
1
Sometext2!(right side)
2
3
...
Please DONT do this as other suggested:
for ($i=0; $i < count($endBioTxt); $i++)
do this:
$count = count($endBioTxt);
for ($i=0; $i < $count; $i++) {
}
No need to calculate the count every iteration.
Nacereddine was correct though about the fact that you don't need to do:
$i++;
inside your loop since the preferred (correct?) syntax is doing it in your loop call.
EDIT
You code just looks 'strange' to me.
Why are you doing:
while ($row = mysql_fetch_assoc($result))
{
$bioText = $row['bio'];
}
???
That would just set $bioText with the last record (bio value) in the recordset.
EDIT 2
Also I don't think you really need a function to calculate the modulo of a number.
EDIT 3
If I understand your answer correctly you want 0 to be in the left li and 1 in the right li 2 in the left again and so on.
This should do it:
$endBioTxt = explode("\n", $bioText);
$i = 0;
foreach ($endBioTxt as $txt)
{
$class = 'left';
if ($i%2 == 1) {
$class = 'right';
}
echo '<li class="'.$class.'"><div>'.$txt.'</div></li>';
echo $i; // no idea why you want to do this since it would be invalid html
$i++;
}
Your for statement should be:
for ($i=0; $i < count($endBioTxt); $i++)
see http://us.php.net/manual/en/control-structures.for.php
$i++; You don't need this line inside a for loop, it's withing the for loop declaration that you should put it.
for ($i=0; $i < count($endBioTxt);$i++)
{
if (checkNum($i) == true)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
echo $i;
}
//$i++; You don't need this line inside a for loop otherwise $i will be incremented twice
}
Edit: Unrelated but this isn't how you check whether a number is prime or not
// Function to check if number is prime
function checkNum($num){
return ($num % 2) ? TRUE : FALSE;
}
This code works, please test it in your environment and then uncomment/comment what you need.
<?php
// This is how query should look like, not big fan of PHP but as far as I remember...
/*
$result = mysql_query("SELECT * FROM info WHERE id = 1");
$row = mysql_fetch_assoc($result);
$bioText = $row['bio'];
$endBioTxt = explode("\n", $bioText);
*/
$endBioTxt[0] = "one";
$endBioTxt[1] = "two";
$endBioTxt[2] = "three";
$endBioTxt[3] = "four";
$endBioTxt[4] = "five";
$totalElements = count($endBioTxt);
for ($i = 0; $i < $totalElements; $i++)
{
if ($i % 2)
{
echo "<li class='left'><div>".$endBioTxt[$i]."</div></li>";
}
else
{
echo "<li class='right'><div>".$endBioTxt[$i]."</div></li>";
}
/*
// This is how you should test if all your array elements are set
if (isset($endBioTxt[$i]) == false)
{
echo "Array has some values that are not set...";
}
*/
}
Edit 1
Try using $endBioTxt = preg_split('/$\R?^/m', $bioTxt); instead of explode.
I need to generate random number of divs with five items per div (and remaining items in the last div) from random number of $totalItems and also not all the items satisfy $OKItems... Hopefully the code explains better than me.
My problem is that this script generates empty divs with no content in them.
<?php
$OKItems = 0;
$totalItems = rand(2,30);
for ($i = 0; $i < $totalItems; $i++) {
echo ($OKItems == 0 || $OKItems % 5 == 0) ? 'div open<br />' : '';
$testValue = rand(0, 1);
if ($testValue != 0) {
echo '1';
$OKItems++;
}
echo ($OKItems % 5 == 0 || $i+1 == $totalItems) ? '<br />div close<br />' : '';
}
?>
This is what I might get:
div open
div close
div open
11111
div close
div open
div close
div open
div close
div open
11
div close
And this is what I would have wanted in this case:
div open
11111
div close
div open
11
div close
<?php
const N = 5;
$totalItems = rand(2,30);
$items = array() ;
for ($i = 0; $i < $totalItems; $i++) {
$testValue = rand(0, 1);
if ($testValue != 0) {
$items[] = 1 ;
}
if( N == sizeof($items) || (($i == $totalItems - 1) && 0 < sizeof($items)) ) {
echo "<div>" . join(",", $items) . "</div>";
$items = array() ;
}
}
I think you need a bit more structure to your code.
My approach would be to break it up into several stages, as opposed to trying to do all the logic in the loop that outputs data.
What I'd suggest:
Decide how many items to be tested
Test each item and only copy the ones that pass into a new array
Partition this new array into sets of 5
Output each partition as a div
Code (untested):
// Decide how many items to test
$totalItems = rand(2,30);
// Test these items and add them to an accepted array
$items = Array();
for ($i = 0; $i < $totalItems; $i++) {
$testValue = rand(0, 1);
if ($testValue != 0) { $items[] = "1" }
}
//Partition them into sections
$partitions = array_chunk($items,5);
//Output as divs
foreach($partitions as $partition):
echo 'div open <br />';
foreach($partition as $item):
echo $item . "<br />";
endforeach;
echo 'div close <br />';
endforeach;
When you split up the code into logical steps, it becomes much easier to maintain and debug.
<?php
$OKItems = 0;
$totalItems = rand(2,30);
for ($i = 0; $i < $totalItems; $i++) {
echo ($OKItems == 0 || $OKItems % 5 == 0) ? 'div open<br>' : '';
$testValue = rand(0, 1);
if ($testValue != 0) {
echo '1';
$OKItems++;
}
if($OKItems % 5 == 0 || $i+1 == $totalItems) {
echo '<br>div close<br>';
$OKItems = 0;
}
}
?>
That should be working ;)
I changed your check line for an if function that also resets your $OKItems. The problem you had (i think) was that you got a 0 as the random value and that would keep $OKitems on 5.
Suppose I have some serially numbered items that are 1-n units wide, that need to be displayed in rows. Each row is m units wide. I need some pseudo-code that will output the rows, for me, so that the m-width limit is kept. This is not a knapsack problem, as the items must remain in serial number order - empty spaces at the end of rows are fine.
I've been chasing my tail over this, partly because I need it in both PHP and jQuery/javascript, hence the request for pseudo-code....
while (!items.isEmpty()) {
rowRemain = m;
rowContents = [];
while (!items.isEmpty() && rowRemain > items[0].width) {
i = items.shift();
rowRemain -= i.width
rowContents.push(i);
}
rows.push(rowContents);
}
Running time is Θ(number of items)
Modulus is your friend. I would do something like:
$items = array(/* Your list of stuff */);
$count = 0;
$maxUnitsPerRow = 4; // Your "m" above
while ($item = $items[$count]) {
if ($count % $maxUnitsPerRow == 0) {
$row = new row();
}
$row->addItemToRow($item);
$count++;
}
Here is an alternative php code ...
function arrayMaxWidthString($items, $maxWidth) {
$out = array();
if (empty($items)) {
return $out;
}
$row = $maxWidth;
$i = 0;
$item = array_shift($items);
$row -= strlen($item);
$out[0] = $item;
foreach ($items as $item) {
$l = strlen($item);
$tmp = ($l + 1);
if ($row >= $tmp) {
$row -= $tmp;
$out[$i] = (($row !== $maxWidth) ? $out[$i] . ' ' : '') . $item;
} elseif ($row === $maxWidth) {
$out[$i] = $item;
++$i;
} else {
++$i;
$row = $maxWidth - $l;
$out[$i] = $item;
}
}
return $out;
}
For what it's worth, I think I have what I was looking for, for PHP - but not sure if there is a simpler way...
<?php
// working with just a simple array of widths...
$items = array(1,1,1,2,1,1,2,1);
$row_width = 0;
$max_width = 2;
echo "Begin\n"; // begin first row
foreach($items as $item=>$item_width) {
// can we add item_width to row without going over?
$row_width += $item_width;
if($row_width < $max_width) {
echo "$item_width ";
} else if($row_width == $max_width) {
echo "$item_width";
echo "\nEnd\nBegin\n"; // end last row, begin new row
$row_width = 0;
} else if($row_width == 2* $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
echo "\nEnd\n"; // end new row
$row_width = 0;
if($item < count($items)) echo "Begin\n"; // new row
} else if($row_width > $max_width) {
echo "\nEnd\nBegin\n"; // end last row, begin new row
echo "$item_width";
$row_width = $item_width;
}
}
echo "\nEnd\n"; // end last row
?>