PHP: loop for certain chunks of a total and then rest - php

I'm not sure if I did this right:
I have a total number of items, say 624. I would like to make a loop that sends 7 requests, the first 6 over 100 items each, the last over the remaining 24.
So I did some math to find out how many loops I have to go through.
// this is the total number of items
$total = $result['total_items'];
// if we split the total into such chunks
$chunksize = 50;
// we will get a rest of so many items
$rest = $total % $chunksize;
// so including the query for the rest, we will have to repeat the query so many times
$queries_no = (($total-$rest)/$chunksize)+1;
The above feels kinda clumsy. Is that the right way?
PS: After that, I can repeat the query the necessary times.
// prep loop
$i = 0;
$requested = 0;
$subscribers = [];
while ($i <= $queries_no){
// last item
if($i == $queries_no){
$chunksize = $rest;
}
$result = $this->monkeyApp->get('lists/'.$list_id.'/members?offset='.$requested.'&count='.$chunksize);
$subscribers = array_merge($subscribers,$result['members']);
$requested = $requested + $chunksize;
$i++;
}

$subscribers = [];
for ($count = $chunksize, $offset = 0; $offset < $total; $offset += $chunksize)
{
if ($offset + $chunksize > $total)
{
$count = $total % $chunksize;
}
$result = $this->monkeyApp->get("lists/$list_id/members?offset=$offset&count=$count");
$subscribers = array_merge($subscribers, $result['members']);
}

Related

looping through an array by slices, not each item

I have an array of potentially 1000s of items that I need to feed to and API that can handle 10 at a time. So, can I do something like this?
$data = [ ... ];
$start = 0;
$step = 10;
foreach (api_function(array_slice($data, $start, $step))){
$start += $step;
}
Using Nigel Ren's answer the final code looks like this:
$results = BC_cust_query($qlist, true);
$start = 0;
$step = 10;
$stop = count($results);
while ($start < $stop){
print_r(array_slice($results, $start, $step));
$start += $step;
}
Where print_r() will be replace by the API call
You would need to keep repeating the call to the API for each slice, until there is no more data left (check the start against the length of the array). Assuming it returns blank, some thing like
$data = [ ... ];
$start = 0;
$step = 10;
$countData = count($data);
// Fetch block of data whilst some data to fetch.
while ($start < $countData &&
$dataBlock = api_function(array_slice($data, $start, $step))){
// Loop over this block
foreach ( $dataBlock as $dataItem) {
// Process item
}
// Move onto next block
$start += $step;
}
or use a for loop...
$data = [ ... ];
$step = 10;
$countData = count($data);
// Fetch block of data whilst some data to fetch.
for ($start = 0;$start < $countData; $start += $step) {
$dataBlock = api_function(array_slice($data, $start, $step));
// Loop over this block
foreach ( $dataBlock as $dataItem) {
// Process item
}
}

Php binary radix sort implementation

I'm trying to implement a radix sort in binary, because i want to check if the speed of bit shifting operations counterbalance the number of steps required.
My counting sort seems to work, but as soon as i have several passes for the radix sort, the result break.
Any help greatly appreciated.
/*
* bits is to store the value of the current digit for each number in the input array
*/
function countSort(&$arr, $digit) {
$bits = $output = array_fill(0, NB_ELEMS, 0);
$count = [0,0];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$bits[$i] = $bit;
$count[$bit]++;
}
// Cumulative count
$count[1] = NB_ELEMS;
// Rearranging
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = $bits[$i];
$output[$count[$bit] - 1] = $nb;
$count[$bit]--;
}
// Switch arrays
$arr = $output;
}
function radixSort(&$arr, $nb_digits) {
// Do counting sort for every binary digit
for($digit = 0; $digit < $nb_digits; $digit++) {
countSort($arr, $digit);
}
}
$tab = [4,3,12,8,7];
$max_value = max($tab);
$nb_digits = floor(log($max_value, 2)) + 1;
radixSort($tab, $nb_digits);
Ok, thanks to a friend, i found my problem : the counting sort implementation which i use stacks the same digit numbers using the counter as top index, and then decrementing it. And that's ok for counting sort (so one iteration), but it scrambles everything when you use it in radix sort (multiple counting sort iterations).
So i used instead a method in which you shift your counters one time right, which gives you for the n+1 digit your starting point, and then increment it.
And boom you're good to go.
function countSort2(&$arr, $digit) {
$bits = $output = array_fill(0, NB_ELEMS, 0); // output array
$count = [0,0];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$bits[$i] = $bit;
$count[$bit]++;
}
// Cumulative count shifted
$count[1] = $count[0];
$count[0] = 0;
// Rearranging
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = $bits[$i];
$output[$count[$bit]] = $nb;
$count[$bit]++;
}
// Switch arrays
$arr = $output;
}
I also made some tests (1M items, max value 1M, 100 iterations), and a method storing values instead of counting them, and merging thereafter seems twice as fast (function just after that). Anyway, both methods are far under native sort performance.
Any comment appreciated
function byDigitSortArrayMerge(&$arr, $digit) {
$output = array_fill(0, NB_ELEMS - 1, 0); // output array
$bits = array_fill(0, NB_ELEMS - 1, 0); // output array
$by_bit = [[], []];
// Store count of occurrences in count[]
for ($i = 0; $i < NB_ELEMS; $i++) {
$nb = $arr[$i];
$bit = ($nb >> $digit) & 1;
$by_bit[$bit][] = $nb;
}
$arr = array_merge($by_bit[0], $by_bit[1]);
}

Unknow output when subtrating result with number

I was trying to subtrat a result output with number am geting unknown result result example 555555 instead of 5 here is my code:
<?php
$txt = "12345678910";
$nu = 2;
$u = 8;
$disp = str_split($txt, $nu);
for($i = 0; $i < $u; $i++) {
$count += count($disp[$i]);
if(strlen($disp[$i]) != $nu)
{
$count = substr($count, 0, 1);
$uu = ($count - 1);
$we =substr($uu, 0, 1);
echo $we;
}
}
?>
thanks for reading and impact in my solutions
This is in answer to removing your errors so it just displays 5.
I seriously have no understanding as to your intentions with this code but to get rid of your errors, you need to refactor things.
Your value of $u is dependant upon the length of $txt and how many digits you set in str_split. Your value of $u is too big as the entries for indexes 6 and 7 do not exist in your array.
1st go ...
<?php
$txt = "12345678910"; // The string of digits
$nu = 2; // How many digits per array
$disp = str_split($txt, $nu); // The array of strings of $nu digits
$u = count($disp); // Calculate the size of $disp - don't guess
$count = 0; // initialise the variable
for ($i = 0; $i < $u; $i++) {
$count += count($disp[$i]); // count($disp[$i]) will always be 1
// Looking for an entry that isn't $nu digits in size
if (strlen($disp[$i]) != $nu) {
$count = substr($count, 0, 1); // Rip off the 1st digit of the index count
$uu = ($count - 1); // decrement it?
echo $uu; // display it
}
}
Another way...
So using foreach is nicer as it takes care of all the hard work.
<?php
$txt = "12345678910";
$nu = 2;
$disp = str_split($txt, $nu);
// DEBUG
var_dump($disp);
$count = 0; // Initialise the variable before we can += it
foreach ($disp as $number) {
$count += 1;
if (strlen($number) != $nu) {
// Taking the index count, getting the 1st digit
$count = substr($count, 0, 1);
// Then subtracting 1 from it
$uu = $count - 1;
echo $uu;
}
}
So this only addresses what you have so it's a bit nicer and without errors ONLY.
Typecasting blindly on the fly in PHP from strings to integers and back again can be a trap.

distribute a number without remainder

I want to distribute an item to specific pieces without cutting the excess piece,
example:
$persons = 7;
$rooms = 2;
for($i = 0; $i < $rooms; $i++)
{
//distribute the persons per room
}
//the endpoint should be
$the_room[0] = 4 //instead of 3.5
$the_room[1] = 3 //instead of 3.5
How about an approach that first equally distributes pieces, then randomises the remainder?
$persons = 7;
$rooms = 2;
$the_room = array();
for($i = 0; $i < $rooms; $i++)
{
$the_room[$i] = floor($persons / $rooms);
}
// Random Distribution
while( $persons - array_sum($the_room) > 0)
{
$the_room[array_rand($the_room)]++;
}
// Sequential Distribution
// $index = 0;
// while( $persons - array_sum($the_room) > 0)
// {
// $the_room[$index++]++;
// }
print_r($the_room);

Populate 4 arrays evenly distributed as much as possible from top to bottom

This question is in relation to this post
How to distribute mysql result set in an multidimensional array of 4 arrays
I got the accepted answer but now i want to make a change to the code and i'm not having a lot of success...
Basically, from a mysql result set, i need to populate 4 arrays evenly distributed as much as possible from top to bottom...
Chris Hayes provided a solutuon that works, but when i tested it today, i realize that it populates the array from left to rigth, and not from top to bottom...
How do i change the code so it populates the 4 arrays as much as possible from top to bottom ?
$i = 0;
$array_r = array( array(), array(), array(), array() );
while ($stmt->fetch()) {
array_push($array_r[$i], array(... values ...));
$i = ($i + 1) % 4;
}
final version without manipulating the input array at all:
for ($num = count($input), $offset = 0; $numBuckets > 0; $numBuckets -= 1, $num -= $bucketSize, $offset += $bucketSize) {
$bucketSize = ceil($num / $numBuckets);
$output[] = array_slice($input, $offset, $bucketSize);
}
pervious answer:
Try the following:
<?php
$input = range('A', 'Z'); // test input data
$output = array(); // the output container
$numBuckets = 4; // number of buckets to fill
for (; $numBuckets > 0; $numBuckets -= 1) {
$output[] = array_splice($input, 0, ceil(count($input) / $numBuckets));
}
print_r($output);
alternative version, without constant rechecking the length of the array
for ($num = count($input); $numBuckets > 0; $numBuckets -= 1, $num -= $bucketSize) {
$bucketSize = ceil($num / $numBuckets);
$output[] = array_splice($input, 0, $bucketSize);
}
This snippet should work for you:
<?php
$array= [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17];
$strays = count($array)%4;
$offset = 0;
$results = array();
for($x = 0; $x < 4; $x++){
if ($x < $strays){
$size = (floor(count($array)/4) + 1);
} else {
$size = (floor(count($array)/4));
}
$results[] = array_slice($array, $offset, $size);
$offset+=$size;
}
print_r($results);
I've tested something and it seems to work... but it looks very spaghetti... please feel free to optimize the code. Thanks.
$num_rows = $stmt->num_rows; //number of records returned by the result set
$min_per_column = (int)($num_rows/4); //minimum records per column
$remainder = $num_rows % 4; //the remainder
$array_r = array(array(), array(), array(), array());
$i = 1;
$col = 0;
//how many records to populate before moving to the next array?
$rows = ($col < $remainder) ? $min_per_column + 1 : $min_per_column;
while ($stmt->fetch()) {
array_push($array_r[$col], array($r_recordingid, $r_title, $r_subtitle, $r_seourl));
$i++;
//initialize values for new array
if ($i > $rows) {
$i = 1;
$col++;
$rows = ($col < $remainder) ? $min_per_column + 1 : $min_per_column;
}
}

Categories