A particular double "for" loop - php

$bar contains 4 (in this example, but potentially much more than 4) values I want to copy into $foo and $blah in this way:
$foo[0] = $bar[0];
$foo[1] = $bar[2];
$blah[0] = $bar[1];
$blah[1] = $bar[3];
So, the even rows of $bar will be copied into $foo[i], and the odd rows will be copied into $blah[i].
I tried with:
for($i=0; $i<2; $i++)
{
for($j=0; $j<4; $j++)
{
if($j % 2 == 0)
{
$foo[$i] = $bar[$j]; // EVEN
}
else
{
$blah[$i] = $bar[$j]; // ODD
}
}
}
However if I show the output of $foo and $blah, $foo[0] is equal to $foo[1], and $blah[0] is equal to $blah[1], while they should contain different values.
Example:
Source:
$bar[0]: 27.8
$bar[1]: Napoli-Posillipo
$bar[2]: 29
$bar[3]: Stadio San Paolo di Napoli, Napoli
(wrong) result of the loop:
$foo[0] = 29 (it should be 27.8)
$foo[1] = 29
$blah[0] = Stadio San Paolo di Napoli, Napoli (it should be Napoli-Posillipo)
$blah[1] = Stadio San Paolo di Napoli, Napoli
Any hints?
Thanks

you have made it to complex, something simple like this should work:
foreach ($bar as $k=>$v){
if($k % 2 == 0){
$foo[] = $v; // EVEN
}else{
$blah[] = $v; // ODD
}
}

what about sth like
foreach($bar as $key => $value) {
if($key % 2 == 0) {
//even
$foo[] = $value;
}
else {
// odd
$blah[] = $value;
}
}

You said it yourself:
"So, the even rows of $bar will be copied into $foo[i], and the odd rows will be copied into $blah[i]".
Cycle all $bar, and copy odd rows into $blah, even into $foo:
for ($i = 0; $i < 4; $i++)
if ($i % 2)
$blah[$i/2] = $bar[$i];
else
$foo[$i/2] = $foo[$i];
In the general case:
switch($i % N) // "N" for Napoli :-D
{
case 0: $VAR1[$i/N] = $bar[$i]; break;
case 1: $VAR2[$i/N] = $bar[$i]; break;
...
}

Related

Best way to use multiple nested loops

I am using php for this, but answers that are not language specific are also welcome.
I have several arrays of objects that I want to loop through and output in order. Each of the arrays have a different kind of object in them, but all of the objects have a unique order attribute.
For example:
$people = [{'name':'George','email':'George#test.com','order':'2'},{'name...];
$sandwiches = [{'type':'bacon','rating':'8/10','order':'1'},{'type...];
$restaurants = ....
$chefs = ...
...
What is the most efficient way to loop through them in order?
Assuming I can determine the maximum order I figured I could do something like:
for($i=0; $i< $maximumOrder; $i++)
{
for($j=0; $j< count($people); $j++)
{
if($people[$j]->order == $i)
{
//Do the things I want to do
break;
}
}
for($j=0; $j< count($sandwiches); $j++)
{
if($sandwiches[$j]->order == $i)
{
//Do the things I want to do
break;
}
}
for($j=0; $j< count($restaurants); $j++)
{
.....
}
But this isn't very good because even if the item with the desired order is found in people it will still continue looping through all the other arrays. I could just add a Boolean variable to show if the desired item has already been found (see below), but I am sure there are better ways to do this.
for($i=0; $i< $maximumOrder; $i++)
{
$found = false;
for($j=0; $j< count($people); $j++)
{
if($people[$j]->order == $i)
{
//Do the things I want to do
$found = true;
break;
}
}
if(!$found == true)
{
for($j=0; $j< count($sandwiches); $j++)
{
if($sandwiches[$j]->order == $i)
{
//Do the things I want to do
$found = true;
break;
}
}
}
if(!$found == true)
{
for($j=0; $j< count($restaurants); $j++)
{
.....
}
The below is based on #Victory's answer, with the addition of an elseif statement, to stop the while loop if it goes passed the desired order number (given these are now sorted arrays). This I believe should increase efficiency (at least with big arrays), but please correct me if I am wrong?
function orderArrayByOrder($a,$b)
{
return ($a->order < $b->order) ? -1 : 1;
}
$a1 = usort($people, "orderArrayByOrder");
$a2 = usort($sandwiches, "orderArrayByOrder");
$a3 = usort($restaurants, "orderArrayByOrder");
$c1 = count($a1)
$c2 = count($c2)
$c3 = count($c3)
$i1 = 0
$i2 = 0
$i3 = 0
// itertor over order
for ($curOrder ... $maxorder)
{
while ($i1 < $c1)
{
if($a1[$i1]->order == $curOrder)
{
//Do what I need to do
break;
}
elseif($a1[$i1]->order > $curOrder)
{
//We know the order won't exist in this array.
break;
}
$i1++;
}
while ($i2 < $c2)
{
if($a2[$i2]->order == $curOrder)
{
//Do what I need to do
break;
}
elseif($a2[$i2]->order > $curOrder)
{
break;
}
$i1++;
}
}
Basically you need to sort each array and find the maxorder, then you loop over the order index and print the items with the given order. This is O(N Log(N)) because of the sorting where N = max number of elements
Here is some pseudo code
sort each array (in php use usort) - O(N log(N))
find maxorder (iterate over each) - O(N)
create an array each index
get the length of each index and store
$a1 = usort($people, function(){})
$a2 = usort($places, function(){})
$a3 = usort($things, function(){})
$c1 = count($a1)
$c2 = count($c2)
$c3 = count($c3)
$i1 = 0
$i2 = 0
$i3 = 0
// itertor over order
for ($curOrder ... $maxorder) {
// while $a1 is on current order and its index is in bound
while ($i1 < $c1 && $a1[$i1]->order == $curOrder) {
echo $a1[$i1]->value;
$i1++;
}
while ($i2 < $c2 && $a2->order == $curOrder) {
echo $a2[$i2]->value;
$i2++;
}
while ($i3 < $c3 && $a3->order == $curOrder) {
echo $a3[$i3]->value;
$i3++;
}
}
If you reindex each array by the object's order value, then you can retrieve an object with a given order in constant time. The code retrieves all related objects in O(n) because you're looking at each element a constant number of times (notice the nested loops have been removed).
$peopleByOrder = array();
$sandwichesByOrder = array();
$restaurantsByOrder = array();
$uniqueOrderKeys = array();
foreach($people as $person) {
$peopleByOrder[$person->order] = $person;
$uniqueOrderKeys[$person->order] = 1;
}
// same for $sandwichesByOrder and $restaurantsByOrder
foreach(array_keys($uniqueOrderKeys) as $oderKey) {
if(isset($peopleByOrder[$orderKey])) {
$person = $peopleByOrder[$orderKey];
}
else if(isset($sandwichesByOrder[$orderKey])) {
$sandwich = $sandwichesByOrder[$orderKey];
}
else if(isset($restaurantsByOrder[$orderKey])) {
$restaurant = $restaurantsByOrder[$orderKey];
}
}

Checking a for progression in a list of variables

Let's say I want to check for simple mathematical progression. I understand I can do it like this:
if ($a<$b and $b<$c and $c<$d and $d<$e and $e<$f) { echo OK; }
Is there a way to do it in a more convenient way? Like so
if ($a..$f isprog(<)) { echo OK; }
I don 't know if I get your problem right. But propably the solution for your progression could be the SplHeap object of the SPL delivered with php.
$stack = new SplMaxHeap();
$stack->insert(1);
$stack->insert(3);
$stack->insert(2);
$stack->insert(4);
$stack->insert(5);
foreach ($stack as $value) {
echo $value . "\n";
}
// output will be: 5, 4, 3, 2, 1
I havent heard of something like this, but how about using simple function:
function checkProgress($vars){ //to make it easie i assume that vars can be given in an array
$result = true;
for ($i=0; $i<= count($vars); $i++){
if ($i>0 && $vars[$i] > $vars[$i-1]) continue;
$result = false;
}
return $result;
}
Solved it quick and dirty:
function ispositiveprogression($vars) {
$num=count($vars)-1;
while ($num) {
$result = true;
if ($vars[$num] > $vars[$num-1]) {
$num--;
}
else { $result = false; break; }
}
return $result;
}
Create an array of values, iterate over them and maintaining a flag that checks if the current element value is greater than / less than that of the next value. Unlike some of the solutions in this thread, this doesn't loop through the whole array. It stops looping when it discovers the first value that's not a progression. This will be a lot more faster if the operation involves a lot of numbers.
function checkIfProg($arr, $compare) {
$flag = true;
for ($i = 0, $c = count($arr); $i < $c; $i++) {
if ($compare == '<') {
if (isset($arr[$i + 1]) && $arr[$i] > $arr[$i + 1]) {
$flag = false;
break;
}
} elseif ($compare == '>') {
if (isset($arr[$i + 1]) && $arr[$i] < $arr[$i + 1]) {
$flag = false;
break;
}
}
}
return $flag;
}
Usage:
$a = 2;
$b = 3;
$c = 4;
$d = 5;
$e = 9;
$f = 22;
$arr = array($a, $b, $c, $d, $e, $f);
var_dump(checkIfProg($arr, '<')); // => bool(true)
If you want the array to be created dynamically, you could use some variable variable magic to achieve this:
$arr = array();
foreach (range('a','f') as $v) {
$arr[] = $$v;
}
This will create an array containing all the values of variables from $a ... $f.

PHP array of elements, sort by circular "animation"?

I've already asked a similar question, but I need a different effect. The original question is here.
I have a simple array. The array length is always a square number. So 16, 25, 36 etc.
$array = array('1', '2', '3', '4' ... '25');
What I do, is arrange the array with HTML so that it looks like a block with even sides.
What I want to do, is sort the elements, so that when I pass the JSON encoded array to jQuery, it will iterate the array, fade in the current block, and so I'd get a circular animation. So I'd like to sort the array kind of like this
So my sorted array would look like
$sorted = array('1', '6', '11'', '16', '21', '22', '23' .. '13');
Is there way to do so?.. Thanks
Edit:
I'm trying to do this by creating matrix-like column/row arrays with this:
$side = 5;
$elems = $side*$side;
$array = range(1,$elems);
for($i=1; $i <= $side; $i++) {
for($x=$i; $x <= $elems; $x=$x+$side) {
$columns[$i][] = $x;
}
}
for($i=1, $y=1; $i <= $elems; $i=$i+$side, $y++) {
for($x=$i; $x < $side+$i; $x++) {
$rows[$y][] = $x;
}
}
My next step is to go down the first column, at the end if it go right on the last elements column, at the end up on the last element etc.. If anyone has a better idea that would be great :)
This will work as long as the grid is always square:
<?php
// The size of the grid - 5x5 in the example above
$gridSize = 5;
// Create a 2D array representing the grid
$elements = array_chunk(range(1, pow($gridSize, 2)), $gridSize);
// Find the half way point - this will be the end of the loop since we
// want to stop in the middle
$end = ceil($gridSize / 2);
// An array to hold the result
$result = array();
// The stopping point of the current interation
$stop = $gridSize;
// Loop from start to the middle
for ($i = 0; $i < $end; $i++) {
// start in the top left corner
$x = $y = $i;
// Traverse Y top to bottom
while ($y < $stop) {
$result[] = $elements[$y++][$x];
}
$y--;
$x++;
// Traverse X left to right
while ($x < $stop) {
$result[] = $elements[$y][$x++];
}
$x--;
$y--;
// Traverse Y bottom to top
while ($y >= $gridSize - $stop) {
$result[] = $elements[$y--][$x];
}
$y++;
$x--;
// Make sure we come in a level
$stop--;
// Traverse X right to left
while ($x >= $gridSize - $stop) {
$result[] = $elements[$y][$x--];
}
}
print_r($result);
See it working
This should work. You can pass any array to circularSort function and it will return your sorted array.
/*
Get the circular sorted array
*/
function circularSort($array)
{
//Get the length of array
$arrayLength = count($array);
//Find the square root of length of array
$arrayRows = sqrt($arrayLength);
//Divide the arrays in $arrayRows
$arrayChunks = array_chunk($array,$arrayRows);
$circularArray = array();
//Call Circular Array function .. Result will be stored in $circularArray
circularArray($arrayChunks,$circularArray);
return $circularArray;
}
/*
Loop arrayChunk in following order
1. Fetch first item from each chunks
2. Fetch all items from last chunk and remove that array from arrayChunk
3. Reverse elements in each remaining chunk
4. Reverse entire arrayChunk array
5. Repeat above 4 steps until $arrayChunks is empty
*/
function circularArray(&$arrayChunks, &$circularArray)
{
if(empty($arrayChunks))
{
return true;
}
//1. Fetch first item from each chunks
foreach($arrayChunks as &$arrayChunk)
{
$circularArray[] = array_shift($arrayChunk);
}
//Fetch Last Chunk from array
$lastChunk = array_pop($arrayChunks);
//2. Fetch all items from last chunk and remove that array from arrayChunk
foreach($lastChunk as $chunkElement)
{
$circularArray[] = $chunkElement;
}
//3. Reverse elements in each remaining chunk
foreach($arrayChunks as &$arrayChunk)
{
if (is_array($arrayChunk))
{
$arrayChunk = array_reverse($arrayChunk);
}
}
$arrayChunks = array_reverse($arrayChunks);
return circularArray(&$arrayChunks, &$circularArray);
}
e.g.
$array = range(1, 25);
$circularArray = circularSort($array);
Another approach in O(n):
<?php
function circ_sort ($inArray) {
$rowSize = pow(count($inArray), 0.5);
if((int)$rowSize != $rowSize) {
throw new InvalidArgumentException();
}
$rowSize = (int)$rowSize;
$round =-1;
for ($x =-1, $y=0, $count =0; $count < count($inArray);) {
if ($y > $x) {
if ($x +1 == $y) {
$direction = 'D'; //Down
$round ++;
$max_iter = $rowSize - (2 * $round);
} else {
$direction = 'L'; //Left
$max_iter = $y - $x -1;
}
} else if ($x > $y) {
$direction = 'R'; //Right
$max_iter = $rowSize - (2 * $round) -1;
} else if ($x == $y) {
$direction = 'U'; //Up
$max_iter = $rowSize - (2 * $round) -1;
}
switch ($direction) {
case 'D': //Down
for ($iter =0; $iter < $max_iter; $iter++) {
$x++;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'R': //Right
for ($iter =0; $iter < $max_iter; $iter++) {
$y++;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'U': //Up
for ($iter =0; $iter < $max_iter; $iter++) {
$x--;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
case 'L': //Left
for ($iter =0; $iter < $max_iter; $iter++) {
$y--;
$circArray[] = $inArray[$x*$rowSize + $y];
$count++;
}
break;
}
}
return ($circArray);
}
$array = range(1, 25);
$circ_array = circ_sort($array);
var_dump($circ_array);
?>
My solution:
The trick is: The first run is 5 then two runs of 4 elements, two of 3 elements, 2 of 2 elements and two of 1 element. (5,4,4,3,3,2,2,1,1) In each run it is incremented a state module 4. Depending on the state the runs goes in one direction or other.
Here is the code:
function circularSort(array $array) {
$n2=count($array);
$n=sqrt($n2);
if((int)$n != $n) throw new InvalidArgumentException();
$Result = Array();
$run =$n; $dir=1;
$x=0; $y=-1;
$i=0;
$st=0;
while ($run) {
while ($dir) {
for ($j=0; $j<$run; $j++) {
if ($st==0) $y++;
if ($st==1) $x++;
if ($st==2) $y--;
if ($st==3) $x--;
$p=$y * $n +$x;
array_push($Result,$array[$p]);
}
$st = ($st +1) & 3;
$dir--;
}
$dir=2;
$run--;
}
return $Result;
}
$a = range(1,25);
var_dump(circularSort($a));

Put value in three different arrays every third time

I have a foreach in which I have three different arrays. I want to put the value - when running the foreach - in the different arrays every third time (look example below for better explanation).
Ex: arr = blue, red, green, yellow, brown
arr1 = blue, yellow arr2 = red, brown arr3 = green
I have this code (but dont work because the modulus doesn't come down to the last if statement more then the first execution of the if).
$count = 0;
foreach($values as $value){
if($count%2){
$arrSecond[] = $value[img][url];
}
else if($count%3){
$arrThird[] = $value[img][url];
}
else {
$arrFirst[] = $value[img][url];
}
$count++;
}
How can I solve this?
You need to be using modulus 3 the whole time only with different offsets:
foreach($values as $value){
$mod = $count%3;
if($mod == 1){
$arrSecond[] = $value['img']['url'];
}
// elseif, not else if!
elseif($mod == 2){
$arrThird[] = $value['img']['url'];
}
else {
$arrFirst[] = $value['img']['url'];
}
$count++;
}
Or you could simply make a single array:
$count = 0;
$data = array();
foreach($source as $value) {
$data[$count % 3][] = $value[img][url];
$count++;
}
That gives you 3 sub arrays with the $value stuff spread out evening within them.
what you can do is the following:
$count = 0;
foreach($values as $value){
switch($count){
case 0:
$arrFirst[] = $value[img][url];
$count = 1;
break;
case 1:
$arrSecond[] = $value[img][url];
$count = 2;
break;
case 2:
$arrThird[] = $value[img][url];
$count = 0;
break;
}
}
This is basically a 3 state state machine which should be more efficient than using modulus for a large number of images.

PHP array comparing

I want to compare two arrays in php. I don't want to do it overall, but block by block.
kind of like this
if (a[1] == b[1]){ // do something }
if (a[2] == b[2]){ // do more }
how can i do this without a whole bunch of ifs ?
thanks in advance :)
$a = array(1, 2, 3, 5);
$b = array(1, 1, 1, 1);
$c = array('something', 'something', 'and so forth');
foreach($a as $key => $value){
if($value == $b[$key]){
echo $c[$key]. '<br />';
}
}
my answer. compare 2 arrays, then rune some code. triggered by the blocks that match
for($i=0;$i<sizeof(a);$i++){
if(a[$i]==b[$i]){
//DO SOMETHING
}
}
want to compare whole array element one by one (assuming both array of same length)
foreach($a as $key => $value){
if($value == $b[$key])
{
// do something
}
else
{
break; // stop doing something and break
}
}
if want to compare some keys
$keys = array('key1', 'key2');
foreach($keys as $value){
if($a[$value] == $b[$value])
{
// true
}
else
{
// false
}
}
$a = array(1, 3 , 5 ,6 , 7);
$b = array(3, 1, 5, 6, 8 ,9);
$array_size = min(count($a), count($b));
for ($i = 0; $i < $array_size; $i++) {
if ($a[$i] == $b[$i]) { //you could/should check whether the index is present.
//some code
}
}
This only works for arrays with the same uniformly distributed numerical index.
foreach(array_intersect_assoc($a,$b) as $key => $data)){
switch($key){
case 1:
//something
break;
case 2:
//something
break;
}
}
for ($i=0; $i < count($a) && $i < count($b); ++$i) {
if ($a[$i] == $b[$i]){
// this is true
} else {
// this is false
}
}
A good ol for loop should do the trick. You can start with an array of things to do:
$arrayOfThingsToDo = array( "someFunc", "anotherFunc", "yetAnotherFunc" );
$arrayOfA = array( "one", "two", "three" );
$arrayOfB = array( "one", "not two", "three" );
function doCompare($a, $b, $f) {
$len = count($a);
for($i = 0; $i < $len; $i++) {
if($a[$i] == $b[$i]) {
$f[$i]();
}
}
}
Good luck!

Categories