Break down number into thousands,hundreds,etc - php

I'm currently putting together a cheque printing solution for my company. When printing the cheque you need to print the number of millions,hundred thousands,ten thousands,thousands, hundreds,tens and units (pounds/dollars/euros etc ) from the amount being paid.
In the case of 111232.23 the following is correctly output from the code I have written below. I cant help feeling that there is a more efficient or reliable method of doing this? Does anyone know of a library/class math technique that does this?
float(111232.23)
Array
(
[100000] => 1
[10000] => 1
[1000] => 1
[100] => 2
[10] => 3
[1] => 2
)
<?php
$amounts = array(111232.23,4334.25,123.24,3.99);
function cheque_format($amount)
{
var_dump($amount);
#no need for millions
$levels = array(100000,10000,1000,100,10,1);
do{
$current_level = current($levels);
$modulo = $amount % $current_level;
$results[$current_level] = $div = number_format(floor($amount) / $current_level,0);
if($div)
{
$amount -= $current_level * $div;
}
}while($modulo && next($levels));
print_r($results);
}
foreach($amounts as $amount)
{
cheque_format($amount);
}
?>

I think you just re-wrote the number_format function that PHP has. My suggestion is to use the PHP function rather than to re-write it.
<?php
$number = 1234.56;
// english notation (default)
$english_format_number = number_format($number);
// 1,235
// French notation
$nombre_format_francais = number_format($number, 2, ',', ' ');
// 1 234,56
$number = 1234.5678;
// english notation without thousands separator
$english_format_number = number_format($number, 2, '.', '');
// 1234.57
?>

I'm not sure exactly what the PHP script would be for this, but if you have 10000, 1000, 100, 10, 1 as the things you need the amounts of. How many 10,000's in amount $dollar?
floor($dollar/10000)
how many thousands?
floor(($dollar%10000)/1000)
etc.

This is not the answer for the question, but the following also break down the decimals.
function cheque_format($amount, $decimals = true, $decimal_seperator = '.')
{
var_dump($amount);
$levels = array(100000, 10000, 1000, 100, 10, 5, 1);
$decimal_levels = array(50, 20, 10, 5, 1);
preg_match('/(?:\\' . $decimal_seperator . '(\d+))?(?:[eE]([+-]?\d+))?$/', (string)$amount, $match);
$d = isset($match[1]) ? $match[1] : 0;
foreach ( $levels as $level )
{
$level = (float)$level;
$results[(string)$level] = $div = (int)(floor($amount) / $level);
if ($div) $amount -= $level * $div;
}
if ( $decimals ) {
$amount = $d;
foreach ( $decimal_levels as $level )
{
$level = (float)$level;
$results[$level < 10 ? '0.0'.(string)$level : '0.'.(string)$level] = $div = (int)(floor($amount) / $level);
if ($div) $amount -= $level * $div;
}
}
print_r($results);
}

Related

How to determine if data is increasing or decreasing in PHP

Let's say we have the following data in an array:
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
As with $data1 we can say the data is increasing while in $data2 it is decreasing.
Using PHP, how do you know the data is increasing or decreasing, and is there a way on how to measure
know the rate of increasing as well as decreasing i.e in terms of percentage.
Edit
From the comments I received I got an idea and here is what I have tried.
What I want to achieve;
I want to know if the trend of the data coming in is upwards or downwards.
Want also to know the rate at which the data is rising or droping. For example $data1 = [1,3,5]; is not the same as $data2 = [1, 20, 55];. You can see $data1 rate of increase is not the same as $data2.
function increaseOrDecrease($streams = []) : array
{
$streams = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]; // For the increasing
//$streams = [23,18,17,17,16,15,16,14,15,10,11,7,4,5]; // For the decreasing
$first = 0;
$diff = [];
foreach ($streams as $key => $number) {
if ($key != 0) {
$diff[] = $number - $first;
}
$first = $number;
}
$avgdifference = array_sum($diff)/count($diff); //Get the average
$side = $avgdifference > 0 ? 'UP' : 'DOWN';
$avgsum = array_sum($streams)/count($streams);
$percentage = abs($avgdifference)/$avgsum * 100;
if ($side == 'UP') {
$data = [
'up' => true,
'percent' => $percentage,
];
}else {
$data = [
'up' => false,
'percent' => $percentage,
];
}
return $data;
}
I would like some help to refactor this code or the best approach to solve the issue.
There are several ways to analyze data and extract a trend. The most classical method is called
least squares. It's a way of fitting a line
through the data. The method computes the slope and the intercept of the line. The trend is just the slope.
The formulas are given here.
A PHP implementation is the following:
function linearRegression($x, $y)
{
$x_sum = array_sum($x);
$y_sum = array_sum($y);
$xy_sum = 0;
$x2_sum = 0;
$n = count($x);
for($i=0;$i<$n;$i++)
{
$xy_sum += $x[$i] * $y[$i];
$x2_sum += $x[$i] * $x[$i];
}
$beta = ($n * $xy_sum - $x_sum * $y_sum) / ($n * $x2_sum - $x_sum * $x_sum);
$alpha = $y_sum / $n - $beta * $x_sum / $n;
return ['alpha' => $alpha, 'beta' => $beta];
}
function getTrend($data)
{
$x = range(1, count($data)); // [1, 2, 3, ...]
$fit = linearRegression($x, $data);
return $fit['beta']; // slope of fitted line
}
Examples:
echo getTrend([1, 2, 3]); // 1
echo getTrend([1, 0, -1]); // -1
echo getTrend([3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20]); // 1.065
echo getTrend([23,18,17,17,16,15,16,14,15,10,11,7,4,5]); // -1.213
You are asking for a type of data structure that can represent ascending as well as descending data. PHP got SplMinHeap and SplMaxHeap for this purpose. These built in classes make life easer when dealing with ascending or descending datasets.
A quick example ...
<?php
declare(strict_types=1);
namespace Marcel;
use SplMinHeap;
$numbers = [128, 32, 64, 8, 256];
$heap = new SplMinHeap();
foreach ($numbers as $number) {
$heap->insert($number);
}
$heap->rewind();
while($heap->valid()) {
// 8, 32, 64, 128, 256
echo $heap->current() . PHP_EOL;
$heap->next();
}
The SplMinHeap class keeps the minimum automatically on the top. So just use heaps instead of arrays that have no structure. Same goes for SplMaxHeap that keeps the highest value on the top.
Finding the differences
If you want to iterate all data and finding the differences between one to the next, you just have to iterate the heap. It 's ordered anyway.
$heap->rewind();
$smallest = $heap->current();
while($heap->valid()) {
// 8, 32, 64, 128, 256
$current = $heap->current();
echo $current . PHP_EOL;
// 0 (8 - 8), 24 (32 - 8), 32 (64 - 32), 64 (128 - 64), 128 (256 - 128)
echo "difference to the value before: " . ($current - $smallest) . PHP_EOL;
$smallest = $current;
$heap->next();
}
I would do simple things like this
$data1 = [3,5,7,6,8,9,13,14,17,15,16,16,16,18,22,20,21,20];
$data2 = [23,18,17,17,16,15,16,14,15,10,11,7,4,5];
getTrend($data1) //Returns up
getTrend($data2) // Returns down
function getTrend($arr)
{
$up = 0;
$down = 0;
$prev = "";
foreach($arr as $val)
{
if($prev != "" && $val > $prev)
{
$up = $val-$prev;
}
else if($prev != "" && $val < $prev)
{
$down = $prev-$val ;
}
$prev = $val);
}
if($up > $down)
{
return "up";
}
else if($down > $up)
{
return "down";
}
else {
return "flat";
}
}

Weighted percentage between two links?

I'm working with two rotating links in a php redirector, is there a way to weight them in percentages?
For example: The first link will have a 70% chance or getting redirected and second 30%.
Code:
<?php
$k = $_GET['sub'];
$aff[] = 'http://google.com';
$aff[] = 'http://yahoo.com';
srand ((double) microtime() * 1000000);
$random_number = rand(0,count($aff)-1);
$lol = ($aff[$random_number]);
$lal = $lol.$k;
header("Location: $lal");
?>
There are similar questions but involve more than two variables.
This seems to work.
$array = array(70 => "http://google.com",
30 => "http://yahoo.com");
$random_number = rand(0, 100);
$last_interval = 0;
$link = "";
foreach( $array as $key => $value ) {
// in range?
if($random_number > $last_interval && $random_number < $key + $last_interval) {
$link = $value;
break;
}
$last_interval = $key;
}
echo "You chose: " . $link;
Concept was taken from https://softwareengineering.stackexchange.com/questions/150616/return-random-list-item-by-its-weight and adapted slightly here is a concept that would work. Credit to #Benjamin Kloster who made the original post.
Prepare a list of intervals that cover 0 to sum(weights, 30 and 70 in your case). Each interval represents one link, its length being it's weight, so for your example:
intervals = [70, 100]
Where an index of 0-70 represents link #1, 70-100 link #2.
Generate a random number n in the range of
0 to sum(weights)
Find the interval in which n falls and you got your link.
Maybe you can do it like this:
$array = array(
array( 30, 'http://google.de/' ),
array( 70, 'http://google.com/' )
);
$number = rand( 1, array_sum( array_column( $array, 0 ) ) );
$lastsum = 0;
foreach( $array as $arr ){
if( $number > $lastsum && $number <= ( $lastsum + $arr[0] ) ){
$url = $arr[1];
break;
}
$lastsum += $arr[0];
}

PHP round value to it's own size (ten, hunderd, thousand)

I want to make a function that round up to its own size of the value.
Example:
5 => 10
51 => 60
100 => 100/200 // i don't mind which it will become
121 => 200
999 => 1000
1001 => 2000
How can i do this within PHP. I know this can be done using ceil/round but i don't know how.
May be You mean this
function Myround(array $numbers)
{
$new_numbers = [];
foreach ($numbers as $num) {
$rozriad = strlen($num)-1;
$new_numbers[] = round(ceil($num/pow(10, $rozriad))*pow(10, $rozriad), -$rozriad);
};
return $new_numbers;
};
$dozens = [5, 51, 100, 121, 999, 1001];
print_r(Myround($dozens));
You can achieve this with the following function:
/**
* by Johannes Schuh, Pineapple Developer
*/
function roundIt($number) {
$amount = 0;
do {
$number = $number / 10;
$amount++;
} while($number >= 10);
$result = 0;
$result = $number - fmod($number, 1);
$result++;
$result = $result * pow(10, $amount);
return $result;
}

Return last values from specific position in array

There's an array of pages,
$pages = array(
1,
2,
3,
...
100,
101
);
And there's a variable $current_page. All I'm trying to do is pagination in digg-like style, so that it would look like this,
< 4 5 6 7 .... 15 16 17 18 >
The first thing that comes to mind is to get last and previous array values from specific position that equals to $current_page.
So I started with a basic for loop, but the problem is that amount of pages could be very large, so I don't think that's an efficient thing to do. Is there any another proper way of doing this? (maybe via native array_* functions)?
The following function builds StackOverflow like pagination. The objectives are:
First and last links must be visible always
Previous and next links must be visible always
At most 4 links before/after the current page should be visible
While the following function displays the complete pager, we are primarily interested in how to calculate the surrounding pages a and b as a function of current page, pager size and page count.
function so_like_pager($current_page, $page_count, $pager_size = 4) {
if ($current_page <= $pager_size) {
// the pager for first 4 pages starts from 1
$a = 1;
$b = min(1 + $pager_size, $page_count);
} else {
// the pager for remaining pages ends at current page + 2
// and starts so that 4 links are shown
$b = min($current_page + ($pager_size >> 1), $page_count);
$a = $b - $pager_size;
}
// return array("show_from" => $a, "show_upto" => $b);
echo '<p>';
if ($current_page !== 1) {
echo '' . 1 . ' ';
} else {
echo '<b>' . 1 . '</b> ';
}
if ($a > 1 + 1) {
echo '<span>...</span> ';
}
for ($i = $a; $i <= $b; $i++) {
if ($i !== 1 && $i !== $page_count) {
if ($current_page !== $i) {
echo '' . $i . ' ';
} else {
echo '<b>' . $i . '</b> ';
}
}
}
if ($b < $page_count - 1) {
echo '<span>...</span> ';
}
if ($current_page !== $page_count) {
echo '' . $page_count . ' ';
} else {
echo '<b>' . $page_count . '</b> ';
}
echo '</p>';
}
function so_like_pager_page($page) {
return 'page-' . $page . '/';
}
Tests:
so_like_pager(1, 100);
so_like_pager(2, 100);
so_like_pager(3, 100);
so_like_pager(4, 100);
so_like_pager(5, 100);
so_like_pager(6, 100);
so_like_pager(50, 100);
so_like_pager(99, 100);
so_like_pager(100, 100);
Output:
PS: Note: I ported this function from ASP classic to PHP in a hurry and did not test against edge cases.
function get_surrounding_pages(array $pages, $current, $amount = 2) {
$pages_idx = array_flip($pages);
if (!isset($pages_idx[$current])) {
return false;
}
$offset = max(0, $pages_idx[$current] - $amount);
$limit = $amount + 1 + ($pages_idx[$current] - $offset);
return array_slice($pages, $offset, $limit);
}
$pages = range(1, 1000);
$current = 42;
get_surrounding_pages($pages, $current, 4);
// array(38, 39, 40, 41, 42, 43, 44, 45, 46)
// this will work even if your number of pages is not continuous (eg: 1, 2, 6).
$pages = array(1, 2, 5, 6, 42, 234, 1048);
$current = 6;
get_surrounding_pages($pages, $current, 2);
// array(2, 5, 6, 42, 234)
// also works if you reach the boundaries
$pages = range(1, 10);
$current = 2;
get_surrounding_pages($pages, $current, 2);
// array(1, 2, 3, 4)
$current = 9;
get_surrounding_pages($pages, $current, 2);
// array(7, 8, 9, 10)
// returns false if you ask a page that doesn't exists
$pages = range(1, 10);
$current = 42;
get_surrounding_pages($pages, $current, 2);
// false
You can use PHP's end function to get the last entry of an array.
<?php
$a = array( 1, 2, 3, 4, 5, 10 );
echo end($a);
You can also use array_slice, or array_splice to cut an array, or array_pop, which removes the last element and returns what was removed.
Maybe you can try end. Should be more efficient than looping.

How can I improve this algorithm?

What I am trying to accomplish is to take a US Currency amount, and break it down into how many of each bill and coin it takes to make that amount using the least of each type.
I wrote this in a hurry, and it works, but I feel like it could be improved. Also, I had to round the remainder because I was getting a strange result once it got to something like (0.13 - (1 * 0.1) instead of 0.3 it would come out to 0.299999995
The code below does seem to work.
function moneybreak ($amount, $sizebill) {
// get units of sizecurrency
$numbills = floor($amount / $sizebill);
$remainder = $amount - ($numbills * $sizebill);
$remainder = round($remainder, 2);
$ret['bills'] = $numbills;
$ret['remain'] = $remainder;
return $ret;
}
$amount = 1999.99;
$money = array();
$tmoney = moneybreak($amount, 500);
$money['fivehundred'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 100);
$money['onehundred'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 20);
$money['twenty'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 10);
$money['ten'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 5);
$money['five'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 1);
$money['one'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.25);
$money['quarter'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.1);
$money['dime'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.05);
$money['nickle'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
$tmoney = moneybreak($tmoney['remain'], 0.01);
$money['penny'] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
Your question is a perfect example of why you shouldn't use floating-point arithmetic to represent currency.
First, you'll need to avoid floating-point numbers like you owe them a lot of money (though actually it might be the opposite). To that end, we'll do our calculations with cents instead of dollars. Any amount you might have needs to be multiplied by 100.
You can then rather easily get away by listing all the money units you want to use ($100, $50, $20, ...) into an array, and sort it in descending order so the biggest ones come out first.
<?php
// money units, in cents
// (reflecting common Canadian currency)
$units = array(10000, 5000, 2000, 1000, 500, 200, 100, 25, 10, 5, 1);
function moneyBreak($amount, $units)
{
// ensure the array is sorted in descending order
rsort($units);
$itemsOfEach = array();
// loop through all the money units
foreach ($units as $unit)
{
// you'll try to use this money unit for the largest possible
// amount of money
$itemsOfEach[$unit] = intval($amount / $unit);
// and once you're done, you'll continue with the remainder
$amount %= $unit;
}
return $itemsOfEach;
}
?>
And here is an example:
$amount = 32347; // $323.47
$result = moneyBreak($amount, $units);
print_r($result);
/* prints this:
Array
(
[10000] => 3
[5000] => 0
[2000] => 1
[1000] => 0
[500] => 0
[200] => 1
[100] => 1
[25] => 1
[10] => 2
[5] => 0
[1] => 2
)
*/
A quick way to do what you want if I understood the question correctly.
$amount = 1999;
$values = array(500, 100, 20, 10, 5, 1);
$result = array();
foreach($values as $value) {
$result[$value] = floor($amount / $value);
$amount = $amount % $value;
}
Just work in the smaller sums too as you see fit.
Edit: Actually, come to think of it the floor part will ofc break when it goes below 1 so you need to add a check for when $amount < 1
Well, since you're doing the same thing over and over again, you could do something like this:
$denominations = array(
'fivehundred'=>500,
'onehundred'=>100,
'twenty'=>20,
'etc'=>'etc' //The rest of the denominations
);
$amount = 1999.99;
$money = array();
foreach($denominations as $word => $num)
{
$tmoney = moneybreak($amount, $num);
$money[$word] = ($tmoney['bills'] > 0) ? $tmoney['bills'] : 0.00;
}
Though, zneak's answer is nice. If combined with the array which includes the keys for the denominations in mine, and modifying his foreach($units as $units) to foreach($units as $key => $unit), you could get an array that has the proper key for each unit ("onehundred","fives", etc.).

Categories