Get last n group of numbers in a loop - php

I have a loop, the loop is like there are 4 items in each row, i want to add class to the elements of the last row so i need something like this :
switch( $count ){
case 8:
$items = [5, 6, 7, 8];
break;
case 7:
$items = [5, 6, 7];
break;
case 6:
$items = [5, 6];
break;
case 5:
$items = [5];
break;
case 9:
$items = [9];
break;
case 10:
$items = [9, 10];
break;
case 11:
$items = [9, 10, 11];
break;
case 12:
$items = [9, 10, 11, 12];
break;
case 13:
$items = [13];
break;
// And so on...
default:
if( $count <= 4 ){
$items = range(1, $count);
}
break;
}
I think using switch statement is not the proper way to do this, besides it works only for $count numbers less than 14 and if i would have to write all numbers one by one if i want to get it worked with no matter what $count value is.
To clearify a little bit more :
We have a set of items ( html elements ), which there are 4 of them in each row, and we want to add a css class to each item in the last row,
so if the total items count is 8 , the 5th, 6th, 7th and 8th items are in the last row.
See thse examples
1 2 3 4
5 6 7 8 This is the last row, so $items equals [5, 6, 7, 8]
OR
1 2 3 4
5 6 This is also the last row so $items equals [5, 6]
OR
1 2 3 4
5 6 7 8
9 10 11 This is last row too so $items equals [9, 10, 11]
OR
1 2 3 4 We have only 4 items , so there is only one row so $items equals [1, 2, 3, 4]

I do not understand why 1, 2, 3, 4 are separate from the rest, but since that is what you seem to want, this is your solution:
if( $count <= 4 ){
$items = [1, 2, 3, 4];
} else {
$items = [] ;
for ( $i = $count - ($count-1) % 4 ; $i <= $count ; $i++) {
$items[] = $i ;
}
}

I was working on something with range() and had to slice it:
$items = array_slice(range($count - 3, $count), -($count % 4));
Then I asked, why couldn't I do it in the range():
$items = range($count - ($count - 1) % 4, $count);

Related

I want to get elements from array in order: three, four, three, four and so on

I want to get elements from an array like this: get first three element, then four elements, then again three elements, and again four, and so on in a loop.
For example:
0 1 2
3 4 5 6
7 8 9
10 11 12 13
and so on....
I tried something like this:
foreach($items as $key => $item) {
if($key <= 2) {
echo 'test';
}
if($key > 2 && $key < 6) {
echo 'other test';
}
if($key > 6 && $key < 9) {
echo 'test';
}
}
However, I don't want to use if() like these, because I don't know how many items will be in the array: it comes from a database.
I think, I need something like array_chunk($items, 3) but for size parameter I need 3 and 4 in loop
Could be like this you can make another array of specifying the number of elements you want in each iteration.
<?php
$number_of_elements = [3,4,3,4];
$your_array = ['a', 'b','c','d','e'];
foreach($number_of_elements as $number){
for($i = 0; $i<=$number; $i++){
$result = $your_array[$i];
print_r($result);
}
print_r('<br>');
}
In JavaScript you can solve your problem using a for loop and the built in slice function of the array.
const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
let offset = 0;
for(let i = 0; i < array.length;){
offset = offset === 3 ? 4 : 3;
const subArray = array.slice(i, i + offset);
console.log(subArray);
i += offset;
}

PHP array slice from position + attempt to return fixed number of items

I'm looking for an efficient function to achieve the following. Let's say we have an array:
$a = [0, 1, 2, 3, 4, 5, 6, 7];
Slicing from a position should always return 5 values. 2 before the position index and 2 values after the position index - and of course, the position index itself.
If a position index is at the beginning of the array i.e. 0 (example 2), the function should return the next 4 values. Similarly, if the position index is at the end of the array (example 3), the function should return the previous 4 values.
Here's some examples of various indexes one could pass to the function and expected results:
$index = 3; // Result: 1, 2, 3, 4, 5. *example 1
$index = 0; // Result: 0, 1, 2, 3, 4. *example 2
$index = 7; // Result: 3, 4, 5, 6, 7. *example 3
$index = 6; // Result: 3, 4, 5, 6, 7. *example 4
As represented in examples: (example 1, example 4), the function should always attempt to catch tokens succeeding and preceding the position index - where it can, whilst always returning a total of 5 values.
The function must be bulletproof to smaller arrays: i.e if $a has 4 values, instead of 5, the function should just return everything.
Something like this?
#edit:
Sorry, I misread your original requirement. Second attempt:
function get_slice_of_5($index, $a) {
if ($index+2 >= count($a)) {
return array_slice($a, -5, 5)
}
else if($index-2 <= 0) {
return array_slice($a, 0, 5)
}
else return array_slice($a, $index-2, 5)
}
Create a start position by calculating where to start and use implode and array slice to return the string.
$a = [0, 1, 2, 3, 4, 5, 6, 7];
$index = 3; // Result: 1, 2, 3, 4, 5. *example 1
echo pages($a, $index) . "\n";
function pages($a, $index){
if($index >= count($a)-2){
$start = count($a)-5; // index is at end of array
}elseif($index <=2){
$start = 0; // index is at start
}else{
$start = $index-2; // index is somewhere in the middle
}
return implode(", ", array_slice($a, $start, 5));
}
https://3v4l.org/aNZsB
this is a "standalone" function to get spliced arrays of any size:
$a = [1,2,3,4,5,6,7,8,9];
echo "<pre>"; print_r(array_slicer($a, 2));
function array_slicer($arr, $start){
// initializations
$arr_len = count($arr);
$min_arr_len = 5; // the size of the spliced array
$previous_elements = 2; // number of elements to be selected before the $start
$next_elements = 2; // number of elements to be selected after the $start
$result = [];
// if the $start index doesn't exist in the given array, return false!
if($start<0 || $start>=$arr_len){
return false;
} elseif($arr_len <= $min_arr_len){ // if the size of the given array is less than the d size of the spliced array, return the whole array!
return $arr;
}
// check if the $start has less than ($previous_elements) before it
if($arr_len - ($arr_len - $start) < $previous_elements){
$next_elements += ($next_elements - ($arr_len - ($arr_len - $start)));
} elseif(($arr_len - 1 - $start) < $next_elements){ // check if the $start has less than ($next_elements) after it
$previous_elements += ($previous_elements - ($arr_len - 1 - $start));
}
for($i = ($start-$previous_elements); $i <= ($start + $next_elements); $i++){
if($i>-1 && $i<$arr_len){
$result[] = $arr[$i];
}
}
return $result;
}
You can define the bounds of where the array_slice() will begin by leveraging min() and max(). Assuming your array will always have at least 5 element, you can use:
array_slice($a, min(count($a) - 5, max(0, $index - 2)), 5)
The chosen index will be in the center of the sliced array unless it cannot be.
Dynamic Code: (Demo)
$a = [0, 1, 2, 3, 4, 5, 6, 7];
$count = count($a);
$span = 5; // most sensible with odd numbers
$center = (int)($span / 2);
foreach ($a as $i => $v) {
printf(
"%d: %s\n",
$i,
implode(
',',
array_slice(
$a,
min($count - $span, max(0, $i - $center)),
$span
)
)
);
}
Output:
0: 0,1,2,3,4
1: 0,1,2,3,4
2: 0,1,2,3,4
3: 1,2,3,4,5
4: 2,3,4,5,6
5: 3,4,5,6,7
6: 3,4,5,6,7
7: 3,4,5,6,7

Generate random "pattern-lock" sequence of digits

Today my friend raised a challenge that I still can't solve: "Generate a random digit sequence in PHP"
The digits are arranged as dial-pad/pattern-lock that consist 1-9 keys in 3 rows and 3 columns:
---------------------------
| |
| 1 2 3 |
| |
| 4 5 6 |
| |
| 7 8 9 |
| |
---------------------------
Now, given a length, we have to generate a random, non-repeating sequence of digits of the provided length, using these criteria:
A generated sequence should follow a specific direction/pattern going only via neighboring digits (possibly diagonally), for example (length:8), 12569874:
1 🡪 2
🡫
4 5 🡪 6
🡩 🡫
7 🡨 8 🡨 9
Digits from the first row should never be followed by a digit from the third row, and vice-versa. The same goes for columns. For example a 1 cannot be followed by a 8, and a 6 cannot be followed by a 4.
can guess more criteria can easily from android pattern-lock system
Here are some example generated sequences for length 9: 12369874/5, 142536987, etc, and for length = 6: 987532, etc
I tried to do this with rand():
$chars = "123456789";
$length = 9;
$clen = strlen( $chars )-1;
$id = '';
for ($i = 0; $i < $length; $i++) {
$id .= $chars[mt_rand(0,$clen)];
}
return ($id);
but, still no luck...
How can I solve this question?
has some limitations but that's for you to work out. I only deal with headaches when I get paid :).
<pre>
<?php
// Keypad
$grid = [
['1', '2', '3'],
['4', '5', '6'],
['7', '8', '9'],
];
// Sequence Target Length
$target_length = 5;
// Place to store the Keypad sequence
$points = [];
// Starting Point
$x = rand(0, 2);
$y = rand(0, 2);
// Run through the process until we have the sequence at the desired length
while (count($points) < $target_length):
// Check if the grid keypad entry has been used
if ($grid[$x][$y]):
// Hasn't been used, so stire it
$points[] = $grid[$x][$y];
// Mark it used
$grid[$x][$y] = NULL;
endif;
// Sanity Check, imagine if you will,.... target length of 9, and you hit 6 5 2 1, You'll vault off into the twilight zone without this
if ((!$grid[$x + 1][$y]) && (!$grid[$x][$y + 1]) && (!$grid[$x - 1][$y]) && (!$grid[$x][$y - 1])):
// We have no where to go
break;
endif;
// Start looking for possible values
do {
$test_x = $x;
$test_y = $y;
$dir = rand(0, 3);
switch ($dir):
case (0):
$test_y--; // Up
break;
case (1):
$test_x++; // Right
break;
case (2):
$test_y++; // Down
break;
case (3):
$test_x--; // Left
break;
endswitch;
// Optional Gibberish
echo "Moving from {$x}, {$y} to {$test_x}, {$test_y} --> " . (($grid[$test_x][$test_y] === NULL) ? 'FAILED' : 'OK!') . '<br>';
// Keep going until we find a valid direction
} while ($grid[$test_x][$test_y] === NULL);
// assign the new coords
$x = $test_x;
$y = $test_y;
// repeat
endwhile;
// report
echo implode('-', $points) . "\n";
?>
</pre>
Here is a solution that will apply these rules:
a path can only step to neighboring cells, i.e. that are adjacent, including diagonally
a path cannot contain the same cell twice
The following algorithm uses recursion for every digit that is added to the sequence. Whenever the sequence gets "stuck", backtracking happens, and an alternative path is tried. Backtracking continues if no more alternatives are available.
It is guaranteed that a path of the given length is returned, provided the given length is between 1 and 9:
function randomSequence($len) {
if ($len < 1 || $len > 9) return []; // No results
$row = [null, 1, 1, 1, 2, 2, 2, 3, 3, 3];
$col = [null, 1, 2, 3, 1, 2, 3, 1, 2, 3];
$neighbors = [[], [2, 4, 5], [1, 4, 5, 6, 3], [2, 5, 6],
[1, 2, 5, 7, 8], [1, 2, 3, 4, 6, 7, 8, 9], [2, 3, 5, 8, 9],
[4, 5, 8], [4, 5, 6, 7, 9], [5, 6, 8]];
// Shuffle the neighbor lists to implement the randomness:
foreach ($neighbors as &$nodes) shuffle($nodes);
$recurse = function ($seq) use (&$len, &$row, &$col, &$neighbors, &$recurse) {
if (count($seq) >= $len) return $seq; // found solution
$last = end($seq);
echo "try " . json_encode(array_keys($seq)) . "\n";
foreach ($neighbors[$last] as $next) {
if (isset($seq[$next])) continue; // Skip if digit already used
$result = $recurse($seq + [$next => $next]);
if (is_array($result)) return $result;
}
};
$choice = rand(1, 9);
return array_keys($recurse([$choice => $choice]));
}
echo "result: " . json_encode(randomSequence(9)) . "\n";
See it run on repl.it
Here's an example in pseudocode for a matrix that looks like this:
1 2
3 4
# Get which other numbers are "legal moves" from each number.
adjacency = {
1: [2, 3],
2: [1, 4],
3: [1, 4],
4: [2, 3]
}
# Get the length of code required.
n = 8
# Start at a random position;
pos = rand(keys(adjacency))
result = []
while (n > 0)
n -= 1
newpos = rand(adjacency[pos])
result[] = newpos
pos = newpos
print(result.join(', '))
If your matrix is going to be large or is going to vary you might want to write some code to generate adjaceny rather than hardcoding it.

Split testing with weighting

I am looking to figure out how to use a database of sales completions to influence split testing.
For example, say I have four different page layouts and for each I have the following stats:
Version 1: 6 sales,
Version 2: 1 sale,
Version 3: 3 sales,
Version 4: 4 sales,
Then it would make sense to have version 1 shown most often, and version 4 being next, while version 2 should hardly be shown at all.
Any ideas how I can achieve this?
Very simple solution, mainly depends on how your data looks currently as to what solution is easiest though.
$sales = array
(
1 => 6,
2 => 1,
3 => 3,
4 => 4
);
$weight = array();
foreach ($sales AS $layout => $num_sales)
{
for ($i = 0; $i < $num_sales; $i++)
{
$weight[] = $layout;
}
}
/*
$weight = array
(
1, 1, 1, 1, 1, 1,
2,
3, 3, 3,
4, 4, 4, 4
);
*/
// Pick a random one to use
$layout_to_use = $weight[rand(0, count($weight))];
Let's say you have display the layouts with following weights:
Version 1: 6
Version 4: 4
Version 3: 3
Version 2: 1
Sum of weight is 14 and weight 6 means that you want to show page approximately 6 times in 14 requests.
If you were using database (which I assume you do it would be)
SELECT SUM(weights) FROM version;
Easiest way to implement random selection with different probabilities of hitting item is to sum weights, sort items by their weights and than just iterate trough all items until you hit zero:
$i = rand( 1, $sum);
foreach( $items as $item){
$i -= $item->weight;
if( $i <= 0){
break;
}
}
// $item is now your desired $item
$items should be sorted list of class Item{ public $weight; ... }, because it's most probable that the first element will be used (second the second and so on) and least iterations will be required
What's happening inside:
$i = 12;
// first iteration, $weight = 6
$i = 6; // condition didn't match
// second iteration, $weight = 4
$i = 2; // condition didn't match
// third iteration, $weight = 3
$i = -1; // condition matched
// using version 3

php array generation challenge

I need to randomly generate an two-dimensional n by n array. In this example, n = 10. The array should have this structure. One example:
$igra[]=array(0,1,2,3,4,5,6,7,8,9);
$igra[]=array(6,9,1,5,0,2,7,3,4,8);
$igra[]=array(2,5....................
$igra[]=array(1,7.....................
$igra[]=array(5,4...................
$igra[]=array(4,2...................
$igra[]=array(9,0.....................
$igra[]=array(8,3.....................
$igra[]=array(7,6....................
$igra[]=array(3,8....................
where
`$igra[x][z]!=$igra[y][z]` (x={0,9},y={0,9});
as you see it's like a matrix of numbers each row of it and column also consist from numbers 0-9, and there is never one number two times in each row or in each column.
how to generate such an array, and each time randomly.
Okay, so here's my version:
$n = 10;
$v1 = range(0, $n-1);
$v2 = range(0, $n-1);
shuffle($v1);
shuffle($v2);
foreach ($v1 as $x => $value)
foreach ($v2 as $y)
$array[$y][$x] = $value++ % $n;
This should be a really fast algorithm, because it involves only generating two random arrays and doesn't involve any swapping at all. It should be random, too, but I cannot prove it. (At least I don't know how to prove something like this.)
This is an optimized version of a very simple algorithm:
First a non-random matrix is created this way (imagine we want only 5*5, not 10*10):
0 1 2 3 4
1 2 3 4 0
2 3 4 0 1
3 4 0 1 2
4 0 1 2 3
In this matrix we now randomly swap columns. As we don't change the columns themselves your rules still are obeyed. Then we randomly swap rows.
Now, as you can see the above algorithm doesn't swap anything and it doesn't generate the above matrix either. That's because it generates the cols and rows to swap in advance ($v1 and $v2) and then directly writes to the correct position in the resulting array.
Edit: Just did some benchmarking: For $n = 500 it takes 0.3 seconds.
Edit2: After replacing the for loops with foreach loops it only takes 0.2 seconds.
This is what I did. Made a valid matrix (2d array) that isn't random. So starting out, row 0 is 0-9, row 1 is 1-0 (ie: 1,2,3...8,9,0), row 2 is 2-1 (2,3...9,0,1)...row 8 is 8-7...etc. Then shuffle that array to randomize the rows and perform a simple column swap to randomize the columns. Should get back exactly what you want. Try this:
<?php
//simple function to show the matrix in a table.
function show($matrix){
echo '<table border=1 cellspacing=0 cellpadding=5 style="float: left; margin-right:20px;">';
foreach($matrix as $m){
echo '<tr>';
foreach($m as $n){
echo '<td>'.$n.'</td>';
}
echo '</tr>';
}
echo '</table>';
}
//empty array to store the matrix
$matrix = array();
//this is what keeps the current number to put into matrix
$cnt = 0;
//create the simple matrix
for($i=0;$i<=9;$i++){
for($j=0;$j<=9;$j++){
$matrix[$i][$j] = $cnt % 10;
$cnt++;
}
$cnt++;
}
//display valid simple matrix
show($matrix);
//shuffle the rows in matrix to make it random
shuffle($matrix);
//display matrix with shuffled rows.
show($matrix);
//swap the columns in matrix to make it more random.
for($i=0;$i<=9;$i++){
//pick a random column
$r = mt_rand(0, 9);
//now loop through each row and swap the columns $i with $r
for($j=0;$j<=9;$j++){
//store the old column value in another var
$old = $matrix[$j][$i];
//swap the column on this row with the random one
$matrix[$j][$i] = $matrix[$j][$r];
$matrix[$j][$r] = $old;
}
}
//display final matrix with random rows and cols
show($matrix);
?>
In my solution, by not generating a random array and checking if it already exists, it should run much faster (especially if the array ever went above 0-9). When you get down to the last row, there is only one possible combination of numbers. You will be generating random arrays trying to find that one answer. It would be pretty much the same as picking a number from 1 to 10 and generating a random number until it hits the one you picked. It could be on the first try, but then again you could pick 1000 random numbers and never get the one you wanted.
Hmm.. I see you got some good answers already, but here's my version:
$n = 10;
$seed_row = range(0, $n - 1);
shuffle($seed_row);
$result = array();
for($x = 0; $x < $n; $x++)
{
$tmp_ar = array();
$rnd_start = $seed_row[$x];
for($y = $rnd_start; $y < ($n + $rnd_start); $y++)
{
if($y >= $n) $idx = $y - $n;
else $idx = $y;
$tmp_ar[] = $seed_row[$idx];
}
$result[] = $tmp_ar;
}
for($x = 0; $x < $n; $x++)
{
echo implode(', ', $result[$x]) . "<br/>\n";
}
sample output:
4, 3, 0, 2, 6, 5, 7, 1, 8, 9
0, 2, 6, 5, 7, 1, 8, 9, 4, 3
7, 1, 8, 9, 4, 3, 0, 2, 6, 5
2, 6, 5, 7, 1, 8, 9, 4, 3, 0
6, 5, 7, 1, 8, 9, 4, 3, 0, 2
9, 4, 3, 0, 2, 6, 5, 7, 1, 8
8, 9, 4, 3, 0, 2, 6, 5, 7, 1
5, 7, 1, 8, 9, 4, 3, 0, 2, 6
1, 8, 9, 4, 3, 0, 2, 6, 5, 7
3, 0, 2, 6, 5, 7, 1, 8, 9, 4
It creates a random random array as a starting point
Then it walks through the seed array taking each element as a starting point for itself to create a new base.

Categories