PHP check if an array contains a specific value every tot elements - php

I have a long array like the following.
What I need to achieve is to check if every 6 elements in this array is found the value 1. If it is not found must be added to one of the 6 elements randomly ( by replacing any of the original values ). Also if the value is found more than 2 times every 6 elements, one of them needs to be replaced with another random number.
I have already created the code that is able to generate the following array randomly.
But I do not know how to check if every 6 elements the array contains the value 1, if not how to add it randomly, if found more than 1 time how to replace one of the values with another random number.
I know it s pretty hard to understand. I hope someone will be able to help.
In few words the final result should be:
Every 6 elements this array has to contain at least the value 1 for one time and no more than one time, located every 6 elements in a random position.
This is what I have done so far, this code generates the array below:
/*
We select the elements to add randomly in the final array from a DB table
*/
$elements = parent::$db->select('_matrix_elements', 'element_id', NULL, 'ORDER BY RAND()');
$count = count($elements) - 1; //count the total number of the elements -1 to start from zero
$columns = 6;
$lines = 8;
//generating the final array
for ($a = 1; $a <= $lines; $a++) {
for ($b = 1; $b <= $columns; $b++) {
$rand = rand(1,$count);
$line['position_'.$a.'_' . $b] = $elements[$rand]['element_id'];
}
}
Array
(
[position_1_1] => 14
[position_1_2] => 6
[position_1_3] => 5
[position_1_4] => 6
[position_1_5] => 9
[position_1_6] => 8
[position_2_1] => 11
[position_2_2] => 7
[position_2_3] => 6
[position_2_4] => 1
[position_2_5] => 7
[position_2_6] => 5
[position_3_1] => 14
[position_3_2] => 5
[position_3_3] => 4
[position_3_4] => 7
[position_3_5] => 4
[position_3_6] => 10
[position_4_1] => 6
[position_4_2] => 2
[position_4_3] => 2
[position_4_4] => 1
[position_4_5] => 7
[position_4_6] => 6
[position_5_1] => 3
[position_5_2] => 7
[position_5_3] => 8
[position_5_4] => 10
[position_5_5] => 3
[position_5_6] => 2
[position_6_1] => 8
[position_6_2] => 2
[position_6_3] => 10
[position_6_4] => 2
[position_6_5] => 10
[position_6_6] => 9
[position_7_1] => 6
[position_7_2] => 10
[position_7_3] => 4
[position_7_4] => 8
[position_7_5] => 1
[position_7_6] => 5
[position_8_1] => 2
[position_8_2] => 7
[position_8_3] => 4
[position_8_4] => 7
[position_8_5] => 9
[position_8_6] => 13
)

Split the list up into a two dimensional array with 6 elements in each 1D array
Count the number of times each element appears in the 6 element sub array
If there are no ones in the array, make a random element 1
If there are 2 or more ones in the array
Replace with a random number (other than one) until there is only one one
Add the final sub array to a 1 dimensional array
Repeat for all the sub arrays in the 2D array
$list_2d = array_chunk($list, 6);
$final_list = array();
foreach ($list_2d as $array) {
$count = array_count_values($array);
if (!array_key_exists(1, $count))
$array[mt_rand(0, 5)] = 1;
else if ($count[1] > 1)
for ($i = 1; $i <= $count[1] - 1; $i++)
$array[array_search(1, $array)] = mt_rand(2, 15);
//Use whatever random number you want here, as long as it's not 1
$final_list = array_merge($final_list, $array);
}

Better to get it right straight away, than to make another solution for fixing it. Try this two functions with your code. Also I tested it so check it out here http://phpfiddle.org/lite/code/i9e-gb3. Hope this is what you need.
function randomizer($columns, $rows, $elements)
{
$line = array();
for ($row = 1; $row <= $rows; $row++)
{
$one_count = array();
for ($col = 1; $col <= $columns; $col++)
{
$line['position_' . $row . '_' . $col] = randomID($elements);
if ($line['position_' . $row . '_' . $col] == 1)
{
//we have 1 - store key value
$one_count[] = 'position_' . $row . '_' . $col;
}
}
if (empty($one_count))
{
//no 1 in last row - we will replace one random
$rand_col = rand(1, $columns);
$line['position_' . $row . '_' . $rand_col] = 1;
}
elseif (count($one_count) > 1)
{
//more than one 1 in last row, we will leave only one
//first - pick one random to keep
$keep_key = array_rand($one_count);
unset($one_count[$keep_key]);
// second - replace others with non 1 values
foreach ($one_count as $repl_key)
{
//this time we won't take ID=1
$line[$repl_key] = randomID($elements, true);
}
}
}
return $line;
}
function randomID($elements, $not1=false)
{
$count = count($elements) - 1;
$rand = rand(0, $count);
$el_id = $elements[$rand]['element_id'];
if ($not1 === true && $el_id == 1)
{
return randomID($elements, true);
}
return $el_id;
}
Almost forgot, about this $rand = rand(0, $count); < this is the correct way. You have done $count = count($elements) - 1; and yet started rand from 1 :)

Related

Pivot array based on row count

I have the following:
$array = array(1,2,3,4,5,6);
I need to "pivot" it to get :
Array ( [0] => 1 [1] => 4 [2] => 2 [3] => 5 [4] => 3 [5] => 6 )
by "pivot" I mean, let's imagine that the array is stored a 2 x 3 matrix (2 rows, and 3 columns). My goal is to pivot it, so that the matrix is now a 3 x 2 matrix (3 rows, 2 columns)
for that of course I need an extra argument, let's say "number of rows" (in this case this is like 2 rows)
I did the following:
function pivotArray($array, $nbrRows)
{
$countTotal = count($array);
$countCols = $countTotal / $nbrRows;
$chunk = array_chunk($array,$countCols);
$out = array();
for ($row=0;$row<$nbrRows;$row++) {
for ($col=0;$col<$countCols;$col++) {
$out[$col][$row] = $chunk[$row][$col];
}
}
$arraySingle = call_user_func_array('array_merge', $out);
return $arraySingle;
}
it works as designed but I wonder whether there is a better way to do that ? for instance avoiding the 2 for loops ? and also avoid the array_merge ?
Instead of reporcessing the array a few times, this code builds an intermediate array and spreads the elements based on $position % $countCols. I've also introduced ceil() to the count of columns in case there is an odd number of elements...
function pivotArray($array, $nbrRows)
{
$countTotal = count($array);
$countCols = ceil($countTotal / $nbrRows);
$arraySingle = [];
foreach ( $array as $position => $value ) {
$arraySingle[$position % $countCols][] = $value;
}
return array_merge(...$arraySingle);
}

Split an array into equal parts with a maximum of 6 items per array

I am trying to split an array of items into multiple equal parts with a maximum of 6 items per array
for example:
5 items in original array --> result: 1 array with 5 items
12 items in original array --> result: 2 arrays with 6 items
7 items in original array --> result: 2 arrays with 3 and 4 items
13 items in original array --> result: 3 arrays with 5,4,4 items
I have absolutely no idea how to get started on this
I guess this is what you are looking for. Not exactly beautiful, but working:
<?php
$size = 13;
$step = 6;
$input = array_keys(array_fill(0, $size, null));
$count = ceil($size / $step);
$chunk = floor($size / $count);
$bonus = $size % $count;
for ($i = 0; $i < $count; $i++) {
$output[] =
$i == 0 ?
array_slice($input, $i * $chunk, $chunk + $bonus) :
array_slice($input, $i * $chunk + $bonus, $chunk);
}
print_r($output);
Here $size is the size of your array and $step is the size of the chunks cut from that array. You can play around with those values.
An example output with the settings above would be:
Array
(
[0] => Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
[1] => Array
(
[0] => 5
[1] => 6
[2] => 7
[3] => 8
)
[2] => Array
(
[0] => 9
[1] => 10
[2] => 11
[3] => 12
)
)
Ok, I did this with a more dynamic programming way, where in we compute the distribution for smaller subproblems and go from 6 to 1, to see if current $j in the code fits in any of the previous distribution.
<?php
$arr = [];
$size = rand(1,150);
$range = range(1,$size);
$dist = [];
$dist[] = [];
for($i=1;$i<=$size;++$i){
if($i <= 6) $dist[] = [$i];
else{
for($j=6;$j>=1;--$j){
if(abs($j - $dist[$i-$j][0]) <= 1){
$dist[] = array_merge($dist[$i-$j],[$j]);
break;
}
}
}
}
// echo $size,PHP_EOL;
// print_r($dist[$size]); print the distribution if you want.
$result = [];
$curr_index = 0;
foreach($dist[$size] as $chunk_size){
$result[] = array_slice($range,$curr_index,$chunk_size);
$curr_index += $chunk_size;
}
echo $size,PHP_EOL;
print_r($result);
Demo: https://3v4l.org/gCWB2 (Note that the output there is different for each version of PHP as a different random number of an array size is generated each time).
Update: You can further optimize the above code for this heavy line $dist[] = array_merge($dist[$i-$j],[$j]);, but this I leave as an exercise to you(hint: store only the smallest starting one with it's count).

Automatically build as many nested loops as I need? [duplicate]

This question already has answers here:
Permutations - all possible sets of numbers
(12 answers)
Closed 4 years ago.
I would like to make a list of every possible combination of inserted numbers. I figured out this way:
for($x=1;$x<=3;$x++){
for($y=1;$y<=3;$y++){
for($e=1;$e<=3;$e++){
echo $x.$y.$e."</br>" ;
}
}
}
But the problem with these nested loops is that I have to put manually as many loops as I have digits in a number. Is there a way to make it automatically/programmatically?
You can use a recursive function to loop again when necessary.
For example, this function prints out the numbers 1-3 for a given number of digits (minimum allowed value is 1, any lower it will still output all single-digit numbers in that range):
function variations($digits, $prefix = '') {
for ($i = 1; $i <= 3; $i++) {
$variation = $prefix . $i;
if ($digits > 1) {
variations($digits - 1, $variation);
} else {
echo $variation . "<br />";
}
}
}
variations(1);
/*
1<br />2<br />3<br />
*/
variations(2);
/*
11<br />12<br />13<br />21<br />22<br />23<br />31<br />32<br />33<br />
*/
https://3v4l.org/Rj2u7 (outputs a linebreak instead of "<br />" for display purposes)
Here's a slight variation that returns the generated numbers in an array instead of echo'ing them directly, so you can use them for some other purpose if you'd like:
function variations($digits, $prefix = '') {
$result = [];
for ($i = 1; $i <= 3; $i++) {
$variation = $prefix . $i;
if ($digits > 1) {
$result = array_merge($result, variations($digits - 1, $variation));
} else {
$result[] = $variation;
}
}
return $result;
}
print_r(variations(1));
/*
Array
(
[0] => 1
[1] => 2
[2] => 3
)
*/
print_r(variations(2));
/*
Array
(
[0] => 11
[1] => 12
[2] => 13
[3] => 21
[4] => 22
[5] => 23
[6] => 31
[7] => 32
[8] => 33
)
*/
https://3v4l.org/LBrjA

Ranking/Position on value of array in PHP

I have an array with these values, Array ( [0] => 12 [1] => 17 [2] => 5 [3] => 27 [4] => 5 ) and I need to find out ranking/position of each value
ie,
for 27 - Rank 1
for 17 - Rank 2
for 12 - Rank 3
for 5 - Rank 4
for 5 - Rank 4 (duplicate value in the array)
and these need to be in the same order as the array, so that output should be as follow
12 - Rank 3
17 - Rank 2
5 - Rank 4
27 - Rank 1
5 - Rank 4
This should work (including duplicate values having the same rank):
$values = array();
$values[0] = 5;
$values[1] = 12;
$values[2] = 19;
$values[3] = 9;
$values[4] = 5;
$ordered_values = $values;
rsort($ordered_values);
foreach ($values as $key => $value) {
foreach ($ordered_values as $ordered_key => $ordered_value) {
if ($value === $ordered_value) {
$key = $ordered_key;
break;
}
}
echo $value . '- Rank: ' . ((int) $key + 1) . '<br/>';
}
try this
$array = Array ( "0" => 12 , "1" => 17 , "2" => 5, "3" => 27, "4" => 5 );
$i=1;
foreach($array as $key=>$values)
{
$max = max($array);
echo "\n".$max." rank is ". $i."\n";
$keys = array_search($max, $array);
unset($array[$keys]);
if(sizeof($array) >0)
if(!in_array($max,$array))
$i++;
}
Demo
Just came across this topic while looking for a nice solution for myself. Just drop this here for the future. What I'm using is the following function:
/* Assign rank to each value of the array $in.
* Args:
* in (array): Array containing as set of numeric values.
*
* Returns:
* Returns an array of the same length with ranks. Highest
* values of $in get rank 1, lower values get higher ranks.
* The same values are attributed to the same ranks.
*/
function array_rank( $in ) {
# Keep input array "x" and replace values with rank.
# This preserves the order. Working on a copy called $x
# to set the ranks.
print "input\n";
print_r($in);
$x = $in; arsort($x);
print "sorted\n";
print_r($x);
# Initival values
$rank = 0;
$hiddenrank = 0;
$hold = null;
foreach ( $x as $key=>$val ) {
# Always increade hidden rank
$hiddenrank += 1;
# If current value is lower than previous:
# set new hold, and set rank to hiddenrank.
if ( is_null($hold) || $val < $hold ) {
$rank = $hiddenrank; $hold = $val;
}
# Set rank $rank for $in[$key]
$in[$key] = $rank;
}
print "ranking result\n";
print_r($in);
return $in;
}
$a = array(140,180,180,100);
array_rank( $a );
The print statements (development) return the following:
input
Array
(
[0] => 140
[1] => 180
[2] => 180
[3] => 100
)
sorted
Array
(
[2] => 180
[1] => 180
[0] => 140
[3] => 100
)
ranking result
Array
(
[0] => 3
[1] => 1
[2] => 1
[3] => 4
)
In case you want to have a reverse ranking (lower values are better) simply replace arsort with asort. In my case I am ranking points, the higher the better. The array (140,180,180,100) gets the ranks (3,1,1,4). Players with the same amount of points get the same rank (in this case rank 1) while rank 2 is left out.
You can probably sort the array by the values:
$ranked = sort($arrayValues);
after this, you can do a for loop, looping through the $ranked array, then printing the arrays in order:
for($i = 0; $i < length($ranked); $i++){
for($j = 0; $j < length($arrayValues); $j++){
if($arrayValues[$i] == $sorted[$j]){
echo $arrayValues[$i] . ' has rank ' . $j;
}
}
}
one problem you have is that you want some return value that has 2 times an identical value and key (5=>rank4). So you cannot do an array with ranks as key nor with values as key. In this example i simply made a 2 dimensional array.
<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// your original input
$original = array(12, 17, 5, 27, 5);
// strip duplicates as they are ranked equally
$rankings = array_unique($original);
// apply some sorting so that the ranks are now given by the keys.
rsort($rankings);
// now just use the origincal array and lookup the rankings for each value
$return = array();
foreach($original as $value)
{
$rankedValue = array();
$rankedValue['value'] = $value;
$rankedValue['rank'] = array_search($value, $rankings) + 1;
$return[] = $rankedValue;
}
var_dump($return);

Dividing numbers from file over array columns

This question is similar to a previous question of mine, but I formulated it wrong. I'm very sorry; here's the actual problem.
I have a file with thousands of numbers underneath each other. Let's simplify by using this:
4
7
1
9
3
3
8
6
2
6
5
1
What I need is to output a matrix (in the form of an array) with a variable number of matrix-rows. The numbers from the file have to be devided over the rows with the first number going to the first row, the second number to the second row, etc. Or, if you like, the 1st number, and every fourth number after that, go to column 1. The second number, and every fourth number after that, to column 2, etc. In the example below, the number of rows is 3:
array (
[0] => 4,9,8,6
[1] => 7,3,6,5
[2] => 1,3,2,1
)
And in this example, the number of rows is 4:
array (
[0] => 4,3,2
[1] => 7,3,6
[2] => 1,8,5
[3] => 9,6,1
)
The number of rows is variable.
Currently, with help of Oscar Jara, I now have this:
$path = "data.csv";
$array = explode("\n", file_get_contents($path));
$numbers = array();
foreach(array_chunk($array, 3) as $number){
$numbers[] = implode(",", $number);
}
But this outputs the numbers from the file over rows instead of columns:
array (
[0] => 4,7,1
[1] => 9,3,3
[2] => 8,6,2
[3] => 6,5,1
)
I get confused when transforming this code into dividing into columns. If you don't, then any help is appreciated.
Try this:
$path = "data.csv";
$data = file($path);
$numbers = Array();
$rowcount = 4;
foreach($data as $i=>$n) {
$numbers[$i % $rowcount][] = $n;
}
// OPTIONAL: Join rows together into comma-separated string
$numbers = array_map(function($a) {return implode(",",$a);},$numbers);
$verticallyChunked = array();
$numColumns = 4;
$i = 0;
// optionally pad the array to next largest multiple of the chunksize
// important if you output an html table and like valid well formed html
$totalRows = ceil(count($array) / $numColumns);
$array = array_pad($array, $totalRows * $numColumns, '');
foreach ($array as $val) {
$verticallyChunked[$i++ % $numColumns][] = $val;
}

Categories