Slice an array into 4 other arrays - php

I have an array which I want to slice in 4 other arrays because I want to display the content of the first array on four columns.
I have tried the code above, but what I get is N columns with 4 items.
$groups = array();
for ($i = 0; $i < count($menu); $i += 4) $groups[] = array_slice($menu, $i, 4);
Can this be modified in order to get exactly 4 columns and distribute the values so they fit?

Like Michael Berkowski suggested:
$groups = array_chunk($menu,4);
Should give you what you need. If you're more into "manual labour":
$groups = array();
while($groups[] = array_splice($menu,0,4))
{//no need for any code here ^^ chunks the array just fine
printf('This loop will run another %d times<br/>',(int)ceil(count($menu)/4));
}
Update:
I see I got this a bit wrong... want to chunk into 4 arrays, not into arrays of four:
$groups = array_chunk($menu,(int)ceil(count($menu)/4));

You can try
// Some Random array
$array = range(1, 20);
// Split it 4 Chuncks
$array = array_chunk($array, 4);
// Slice The first 4 Chunks
$array = array_slice($array, 0, 4);
// Output Result
foreach ( $array as $set ) {
printf("<li>%s</li>", implode(",", $set));
}

Related

Sort values in ascending groups representing every third value

I have an array that I'd like to reorder by every 3rd item. So it's 1st, 4th, 7th and then 2nd, 5th, 8th and 3rd, 6th, 9th.
Input:
$items = ['1','2','3','4','5','6','7','8','9'];
Desired result:
['1','4','7','2','5','8','3','6','9']
Or it can be 3 separate arrays as well. Like this:
['1', '4', '7']
['2', '5', '8']
['3', '6', '9']
I tried array_chunk($items, count($items) / 3) but it just returns the same array divided into 3 equal arrays.
['1', '2', '3']
['4', '5', '6']
['7', '8', '9']
I don't know if I should use array_chunk and then something else to achieve what I want.
UPDATE: It does not have to be exactly 9 items in the input array. it can be shorter or longer, the key point is that it has to take every 3rd item as long as it finds any and so on...
So if there are 10 items:
$items = ['1','2','3','4','5','6','7','8','9','10'];
Desired result:
['1','4','7','10','2','5','8','3','6','9']
The simplest way I can think of is to loop over the list three times:
Start at element 0, skipping by 3, stop when past end of list
Start at element 1, skipping by 3, stop when past end of list
Start at element 2, skipping by 3, stop when past end of list
That can be easily achieved using C-style for loop syntax:
$maxKey = count($items) - 1;
$rearrangedItems = [];
for ( $key=0; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
for ( $key=1; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
for ( $key=2; $key<=$maxKey; $key+=3 ) {
$rearrangedItems[] = $items[$key];
}
If you needed to generalize to different offsets, you could put the whole thing into an outer loop rather than copying and pasting:
$offset = 3;
$maxKey = count($items) - 1;
$rearrangedItems = [];
for ( $start=0; $start<$offset; $start++ ) {
for ( $key=$start; $key<=$maxKey; $key+=$offset ) {
$rearrangedItems[] = $items[$key];
}
}
Try this
$items = array('1','2','3','4','5','6','7','8','9');
$tmp = [];
for ($i = 0; $i < count($items); $i++) {
$tmp[$i%3][] = $items[$i];
}
$result = array_merge($tmp[0], $tmp[1], $tmp[2]);
print_r($result);
Try this,
$items = array('1','2','3','4','5','6','7','8','9');
$tmp = [];
for ($i = 0; $i < count($items); $i++) {
$tmp[$i%3][] = $items[$i];
}
$result = call_user_func_array('array_merge', $tmp);
dd($result);
Output :
array('1','4','7','2','5','8','3','6','9')
I see this task as nothing more than a sorting task.
Sort each value by it modulus value when divided by 3. If the result is 0, then fallback to 3. Nothing extravagant, just simple, direct, logical, D.R.Y. programming.
Code: (Demo)
array_multisort(
array_map(fn($v) => $v % 3 ?: 3, $items),
$items
);
Pretty straightforward:
$items = array('1','2','3','4','5','6','7','8','9');
$items = array_chunk($items, 3); # Split into arrays of size 3
$items = array_map(null, ...$items); # Transpose the arrays
$items = array_merge(...$items); # Merge the arrays again
print_r($items);

shuffle an array with a "limit" in PHP

What I try to achieve: randomize the order of all elements in an array, but allow each element to change its position only by a limited number of "steps".
Say I have an array like below, and I wish to randomize with a limit of 2 steps:
$array = [92,12,2,18,17,88,56];
An outcome could be: [2,12,92,17,18,56,88] (all elements of the array moved a maximum of 2 steps), but it could not be: [56,92,2,12,17,18,88] because in this example 56 moved too far.
I considered using a combination of array_chunk and shuffle, but this is problematic because elements will be shuffled inside their chunk, resulting in elements at the beginning or end of a chunk only moving in one direction. This is what I came up with (and problematic):
// in chunks of 3 an element can move a max. of 2 steps.
$chunks = array_chunk($array, 3);
$newChunks = [];
foreach ($chunks as $chunk){
$keys = array_keys($chunk);
shuffle($keys);
$newChunk = [];
foreach ($keys as $key){
$newChunk[$key] = $chunk[$key];
}
$newChunks[] = $newChunk;
}
Another idea I had was to get the key of the item in the array and with rand add of subtract my limit. For example:
foreach ( $array as $key => $value ) {
$newArray[] = ["key" => $key+rand(-2,2), "value" => $value];
};
This creates a new array with each of its elements being an array with the original value plus a value key that is the original key plus or minus 2. I could flatten this array, but the problem with this is that I can have duplicate keys.
I created this function to do this, but I guess it needs more improvements:
/**
* #param array $array
* #param int $limit
* #return array
*/
function shuffleArray(array $array, int $limit): array
{
$arrayCount = count($array);
$limit = min($arrayCount, $limit);
for ($i = 0; $i < $limit; $i++) {
for ($j = 0; $j < $arrayCount;) {
$toIndex = min($arrayCount - 1, $j + rand(0, 1));
[$array[$j], $array[$toIndex]] = [$array[$toIndex], $array[$j]];
$j += (($toIndex === $j) ? 1 : 2);
}
}
return $array;
}
Test:
$array = [92, 12, 2, 18, 17, 88, 56];
$limit = 2;
$result = shuffleArray($array, $limit); // [12, 92, 17, 2, 18, 56, 88]
Here is a possible solution in one pass :
Try to swap each element at position i with an element between i (stay in place) and i+x. I look only forward to avoid swaping an element several times. And I need an extra array to flag the already swapped elements. I don't need to process them in the future as they were already moved.
function shuffle_array($a, $limit)
{
$result = $a ;
$shuffled_index = array() ; // list of already shuffled elements
$n = count($result);
for($i = 0 ; $i < $n ; ++$i)
{
if( in_array($i, $shuffled_index) ) continue ; // already shuffled, go to the next elements
$possibleIndex = array_diff( range($i, min($i + $limit, $n-1)), $shuffled_index) ; // get all the possible "jumps", minus the already- shuffled index
$selectedIndex = $possibleIndex[ array_rand($possibleIndex) ]; // randomly choose one of the possible index
// swap the two elements
$tmp = $result[$i] ;
$result[$i] = $result[$selectedIndex] ;
$result[$selectedIndex] = $tmp ;
// element at position $selectedIndex is already shuffled, it needs no more processing
$shuffled_index[] = $selectedIndex ;
}
return $result ;
}
$array = [92,12,2,18,17,88,56];
$limit = 2 ;
shuffle_array($array, $limit); // [2, 18, 92, 12, 17, 56, 88]
I expect more elements to stay in place than in the solution of Kerkouch, as some elements can have very few remaining free choices.

Get x highest values from an array including identical values

I'm using the following code to retrieve the highest 3 numbers from an array.
$a = array(1,2,5,10,15,20,10,15);
arsort($a, SORT_NUMERIC);
$highest = array_slice($a, 0, 3);
This code correctly gives me the highest three numbers array(20,15,10); however, I'm interested in getting the highest 3 numbers including the ones that are identical. In this example, I'm expecting to get an array like array(10, 10, 15, 15, 20)
Might be simpler but my brain is tired. Use arsort() to get the highest first, count the values to get unique keys with their count and slice the first 3 (make sure to pass true to preserve keys):
arsort($a, SORT_NUMERIC);
$counts = array_slice(array_count_values($a), 0, 3, true);
Then loop those 3 and fill an array with the number value the number of times it was counted and merge with the previous result:
$highest = array();
foreach($counts as $value => $count) {
$highest = array_merge($highest, array_fill(0, $count, $value));
}
You can use a function like this:
$a = array(1,2,5,10,15,20,10,15); //-- Original Array
function get3highest($a){
$h = array(); //-- highest
if(count($a) >= 3){ //-- Checking length
$c = 0; //-- Counter
while ($c < 3 || in_array($a[count($a)-1],$h) ){ //-- 3 elements or repeated value
$max = array_pop($a);
if(!in_array($max,$h)){
++$c;
}
$h[] = $max;
}
sort($h); //-- sorting
}
return $h; //-- values
}
print_r(get3Highest($a));
Of course you can improve this function to accept a dinamic value of "highest" values.
The below function may be usefull
$a = array(1,2,5,10,15,20,10,15);
function getMaxValue($array,$n){
$max_array = array(); // array to store all the max values
for($i=0;$i<$n;$i++){ // loop to get number of highest values
$keys = array_keys($array,max($array)); // get keys
if(is_array($keys)){ // if keys is array
foreach($keys as $v){ // loop array
$max_array[]=$array[$v]; // set values to max_array
unset($array[$v]); // unset the keys to get next max value
}
}else{ // if not array
$max_array[]=$array[$keys]; // set values to max_array
unset($array[$keys]); // unset the keys to get next max value
}
}
return $max_array;
}
$g = getMaxValue($a,3);
Out Put:
Array
(
[0] => 20
[1] => 15
[2] => 15
[3] => 10
[4] => 10
)
You can modify it to add conditions.
I thought of a couple of other possibilities.
First one:
Find the lowest of the top three values
$min = array_slice(array_unique($a, SORT_NUMERIC), -3)[0];
Filter out any lower values
$top3 = array_filter($a, function($x) use ($min) { return $x >= $min; });
Sort the result
sort($top3);
Advantages: less code
Disadvantages: less inefficient (sorts, iterates the entire array, sorts the result)
Second one:
Sort the array in reverse order
rsort($a);
Iterate the array, appending items to your result array until you've appended three distinct items.
$n = 0;
$prev = null;
$top = [];
foreach ($a as $x) {
if ($x != $prev) $n++;
if ($n > 3) break;
$top[] = $x;
$prev = $x;
}
Advantages: more efficient (sorts only once, iterates only as much as necessary)
Disadvantages: more code
This gives the results in descending order. You can optionally use array_unshift($top, $x) instead of $top[] = $x; to get it in ascending order, but I think I've read that array_unshift is less efficient because it reindexes the array after each addition, so if optimization is important it would probably be better to just use $top[] = $x; and then iterate the result in reverse order.

Given an array of integers, what's the most efficient way to get the number of other integers in the array within n?

Given the following array:
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
And assuming $n = 2, what is the most efficient way to get a count of each value in the array within $n of each value?
For example, 6 has 3 other values within $n: 5,7,7.
Ultimately I'd like a corresponding array with simply the counts within $n, like so:
// 0,0,1,2,2,5,6,7,7,9,10,10 // $arr, so you can see it lined up
$count_arr = array(4,4,4,4,4,3,3,4,4,4, 2, 2);
Is a simple foreach loop the way to go? CodePad Link
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
$n = 2;
$count_arr = array();
foreach ($arr as $v) {
$range = range(($v-$n),($v+$n)); // simple range between lower and upper bound
$count = count(array_intersect($arr,$range)); // count intersect array
$count_arr[] = $count-1; // subtract 1 so you don't count itself
}
print_r($arr);
print_r($count_arr);
My last answer was written without fully groking the problem...
Try sorting the array, before processing it, and leverage that when you run through it. This has a better runtime complexity.
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
asort($arr);
$n = 2;
$cnt = count($arr);
$counts = array_pad(array(), $cnt, 0);
for ($x=0; $x<$cnt; $x++) {
$low = $x - 1;
$lower_range_bound = $arr[$x]-$n;
while($low >= 0 && ($arr[$low] >= $lower_range_bound)) {
$counts[$x]++;
$low--;
}
$high = $x + 1;
$upper_range_bound = $arr[$x]+$n;
while($high < $cnt && $arr[$high] <= $upper_range_bound) {
$counts[$x]++;
$high++;
}
}
print_r($arr);
print_r($counts);
Play with it here: http://codepad.org/JXlZNCxW

divide array into smaller arrays [duplicate]

This question already has answers here:
How to get sub array in PHP?
(3 answers)
Closed 9 years ago.
I have an first $array1 of 200 integer values,$array1 integer values are random values and second array say as below
$array2= array('30','40,'70','30','30');
Both the arrays are generating dynamically.
I want to divide my $array1 into smaller arrays as per $array2 values, as first 30 integers from $array1 will make separate array, later 40 intergers from $array1 will make second new array and so on. From above example there will be 5 new arrays.
Can anyone please help me.
I tried below below it can just divide based on values assigned. But it is not useful
$arrays = array_chunk($array1, 30);
You should iterate over the array of chunk sizes and cut off an appropriate portion of your input array each time:
$input = range(0, 199);
$chunks = array(30, 40, 70, 30, 30);
$output = array();
$processed = 0;
while($chunks) {
$processed += $size = array_shift($chunks);
$output[] = array_slice($input, $processed, $size);
}
Why do not you try this
$startingPoint = 0;
foreach($array2 as $val)
{
for($i = $startingPoint ; $i < $startingPoint + $val ; $i++)
{ $array1[$i] }
$startingPoint += $val
}

Categories