php numbers like (10M, ..) - php

I would like to work with numbers like 10M (which represents 10 000 000), and 100K etc. Is there a function that already exists in PHP, or do I have to write my own function?
I'm thinking something along the lines of :
echo strtonum("100K"); // prints 100000
Also, taking it one step further and doing the opposite, something to translate and get 100K from 100000?

You could whip up your own function, because there isn't an builtin function for this. To give you an idea:
function strtonum($string)
{
$units = [
'M' => '1000000',
'K' => '1000',
];
$unit = substr($string, -1);
if (!array_key_exists($unit, $units)) {
return 'ERROR!';
}
return (int) $string * $units[$unit];
}
Demo: http://codepad.viper-7.com/2rxbP8
Or the other way around:
function numtostring($num)
{
$units = [
'M' => '1000000',
'K' => '1000',
];
foreach ($units as $unit => $value) {
if (is_int($num / $value)) {
return $num / $value . $unit;
}
}
}
Demo: http://codepad.viper-7.com/VeRGDs
If you want to get really funky you could put all that in a class and let it decide what conversion to run:
<?php
class numberMagic
{
private $units = [];
public function __construct(array $units)
{
$this->units = $units;
}
public function parse($original)
{
if (is_numeric(substr($original, -1))) {
return $this->numToString($original);
} else {
return $this->strToNum($original);
}
}
private function strToNum($string)
{
$unit = substr($string, -1);
if (!array_key_exists($unit, $this->units)) {
return 'ERROR!';
}
return (int) $string * $this->units[$unit];
}
private function numToString($num)
{
foreach ($this->units as $unit => $value) {
if (is_int($num / $value)) {
return $num / $value . $unit;
}
}
}
}
$units = [
'M' => 1000000,
'K' => 1000,
];
$numberMagic = new NumberMagic($units);
echo $numberMagic->parse('100K'); // 100000
echo $numberMagic->parse(100); // 100K
Although this may be a bit overkill :)
Demo: http://codepad.viper-7.com/KZEc7b

For second one
<?php
# Output easy-to-read numbers
# by james at bandit.co.nz
function bd_nice_number($n) {
// first strip any formatting;
$n = (0+str_replace(",","",$n));
// is this a number?
if(!is_numeric($n)) return false;
// now filter it;
if($n>1000000000000) return round(($n/1000000000000),1).' trillion';
else if($n>1000000000) return round(($n/1000000000),1).' billion';
else if($n>1000000) return round(($n/1000000),1).' million';
else if($n>1000) return round(($n/1000),1).' thousand';
return number_format($n);
}
?>

Related

How to take a single record from an array

I have a function that counts the number of points for each letter. I want her to count the points for each word. See this is my code:
function getValue() {
$letter = $this->getName(); // String from FORM
// Switch looks at a letter and assigns the value points for that letter
switch(true){
case($letter == 'a'||$letter == 'e'||$letter == 'i'||$letter == 'o'||$letter == 'u'||$letter == 'l'||$letter == 'n'||$letter == 's'||$letter == 't'||$letter == 'r'):
return 1;
case($letter == 'd'||$letter == 'g'):
return 2;
case($letter == 'b'||$letter == 'c'||$letter == 'm'||$letter == 'p'):
return 3;
case($letter == 'f'||$letter == 'h'||$letter == 'v'||$letter == 'w'||$letter == 'y'):
return 4;
case($letter == 'k'):
return 5;
case($letter == 'j'||$letter == 'x'):
return 8;
case($letter == 'q'||$letter == 'z'):
return 10;
default:
return 0;
}
}
function makeWordsPoint() {
$total_word_points = 0;
$words = $this->word_for_letters;
foreach ($words as $word) {
$total_word_points = $word->getValue();
}
echo $word . "=" . $total_word_points
}
How I can do it? Thanks for help
EDIT:
Okey, look now. There is my two classes Word and Letter
<?php
class Word
{
private $word;
private $words_with_points = array();
function __construct($user_letters)
{
$this->word = $user_letters;
// creates array of object word for letters
$this->word_for_letters = $this->makeWordForLetters();
// creates array of letter objects for the word
$this->words_with_points = $this->makeWordsWithPoints();
}
function makeWordForLetters()
{
$word_objects = array();
$word = $this->getWord();
$file = file_get_contents( __DIR__."/../src/dictionary.txt");
$items = explode("\n", $file);
$letters = str_split($word);
foreach ($items as $item) {
$list = $letters;
// remove the original word (once)
$thisItem = preg_replace("/$word/", '', $item, 1);
for ($i = 0; $i < strlen($thisItem); $i++) {
$index = array_search($thisItem[$i], $list);
if ($index === false) {
continue 2; // letter not available
}
unset($list[$index]); // remove the letter from the list
}
array_push($word_objects, $item);
}
return $word_objects; // passed!
}
function makeWordsWithPoints()
{
$word = $this->makeWordForLetters();
$letter_objects = array();
foreach ($word as $character) {
array_push($letter_objects, new Letter($character));
}
return $letter_objects;
}
function getWord()
{
return $this->word;
}
function getWordForLetters()
{
return $this->word_for_letters;
}
function getWordsWithPoints()
{
return $this->words_with_points;
}
}
?>
<?php
class Letter
{
private $name;
private $value;
function __construct($letter)
{
$letter = strtolower($letter);
$this->name = $letter;
$this->value = $this->setValue();
}
function getName()
{
return $this->name;
}
function getValue()
{
return $this->value;
}
function setValue()
{
$letter = $this->getName();
switch(true){
case($letter == 'a'||$letter == 'e'||$letter == 'i'||$letter == 'o'||$letter == 'u'||$letter == 'l'||$letter == 'n'||$letter == 's'||$letter == 't'||$letter == 'r'):
return 1;
case($letter == 'd'||$letter == 'g'):
return 2;
case($letter == 'b'||$letter == 'c'||$letter == 'm'||$letter == 'p'):
return 3;
case($letter == 'f'||$letter == 'h'||$letter == 'v'||$letter == 'w'||$letter == 'y'):
return 4;
case($letter == 'k'):
return 5;
case($letter == 'j'||$letter == 'x'):
return 8;
case($letter == 'q'||$letter == 'z'):
return 10;
default:
return 0;
}
}
}
?>
And now when I write in now letters like this: loso function makeWordForLetters() search in my array correctly words for this letters and I display this words with points by makeWordsWithPoint like this:
l - 1
lo - 0
loo - 0
loos - 0
los - 0
oslo - 0
s - 1
solo - 0
But as you can see the score is incorrect because it displays the result for a single letter and not for a word.
How can I solve this problem?
take it as string, then use preg_split function, count new array length.eg:
$string="php教程#php入门:教程#字符串:多分隔符#字符串:拆分#数组";
$arr = preg_split("/(#|:)/",$string);
print_r($arr);
Try this code instead. I think it's cleaner.
<?php
// set the score of each char into array
const SCORES = [
// 1
'a'=> 1,
'e' => 1,
'i' => 1,
'o' => 1,
'u' => 1,
'l' => 1,
'n' => 1,
's' => 1,
't' => 1,
'r' => 1,
// 2
'd'=> 2,
'g'=> 2,
// 3
'b'=> 3,
'c'=> 3,
'm'=> 3,
'p'=> 3,
// 4
'f'=> 4,
'h'=> 4,
'v'=> 4,
'w'=> 4,
'y'=> 4,
// 5
'k'=> 5,
// 8
'j'=> 8,
'x'=> 8,
// 10
'q'=> 10,
'z'=> 10,
];
$word = 'abcdef'; // get the string from the request here
# print_r($word);
$chars = str_split($word); // split string into array of chars
# print_r($chars);
$scores = array_map(function($char) { // create a scores array that convert char into value
return SCORES[strtolower($char)] ?? 0; // get the score of each char and set to the array, if not exist set to 0
}, $chars);
# print_r($scores);
$totalScore = array_sum($scores); // get the sum of the scores
echo $word . "=" . $totalScore;
Let me know if you have any question.

Parsing time format

How do I calculate the input to time in seconds, when the input is in different formats as shown in the example output below?
With this format, all parts are optional:
5d 4h 3m 2s -> 5 days, 4 hours, 3 minutes and 2 seconds
4h 2s -> 4 hours and 2 seconds
3m -> 3 minutes
With this format, parts are sometimes optional:
05: 04: 03: 02 -> 5 days, 4 hours, 3 minutes and 2 seconds
4:03:02 -> 4 hours, 3 minutes and 2 seconds
05: 00: 03: 02 -> 5 days, 3 minutes and 2 seconds
02 -> 2 seconds
I'd like to get this output:
> php time.php 1d 6h 20m 2s
> 109.202 seconds
> php time.php 3:50
> 230 seconds
> php time.php 4: 23: 45: 02
> 431.102 seconds
> php time.php 23m
> 1.380 seconds
So far I made it possible to convert input seconds to a time format, but after 5 hours of trying to make the above question work I kind of gave up:
<?php
$setTime = $argv[1];
function format_time($t,$f=':') {
return sprintf("%02d%s%02d%s%02d", floor($t/3600), $f, ($t/60)%60, $f, $t%60);
}
echo format_time($setTime);
?>
You could use preg_match_all to split the input into pairs of numbers and suffixes and then iterate those to count the number of seconds:
function seconds($input) {
preg_match_all("/(\d+)(\S)/", "$input:", $matches, PREG_SET_ORDER);
$letters = "smhd";
$durations = ["s" => 1, "m" => 60, "h" => 60*60, "d" => 60*60*24];
$seconds = 0;
foreach (array_reverse($matches) as list($all, $num, $code)) {
$i = strpos($letters, $code);
if ($i === false) $code = $letters[$i = 0];
$letters = substr($letters, $i+1);
$seconds += $durations[$code] * $num;
}
return $seconds;
}
function test($input) {
$seconds = number_format(seconds($input));
echo "$input => $seconds seconds\n";
}
test("1d 6h 20m 2s"); // 109.202 seconds
test("3:50"); // 230 seconds
test("4: 23: 45: 02"); // 431.102 seconds
test("23m"); // 1.380 seconds
3v4l.org demo
The following might be total overkill, but it also might illustrate how you can approach this problem if you want your code to be extendable and easy to reuse:
<?php
declare(strict_types=1);
error_reporting(-1);
ini_set('display_errors', 'On');
interface TimeStringParser
{
public function accepts(string $input): bool;
public function parse(string $input): int;
}
final class InputWithUnits implements TimeStringParser
{
public function accepts(string $input): bool
{
foreach (preg_split('/\s+/', $input) as $chunk) {
if (! preg_match('/^\d+(d|h|m|s)$/', $chunk)) {
return false;
}
}
return true;
}
public function parse(string $input): int
{
if (! $this->accepts($input)) {
throw new \InvalidArgumentException('Invalid input.');
}
$result = 0;
if (preg_match_all('/((?<value>\d+)(?<unit>d|h|m|s))/', $input, $matches)) {
foreach ($matches['unit'] as $i => $unit) {
$value = (int) $matches['value'][$i];
switch ($unit) {
case 'd': $result += $value * 86400; break;
case 'h': $result += $value * 3600; break;
case 'm': $result += $value * 60; break;
case 's': $result += $value * 1; break;
}
}
}
return $result;
}
}
final class InputWithoutUnits implements TimeStringParser
{
public function accepts(string $input): bool
{
foreach (explode(':', $input) as $chunk) {
if (! preg_match('/^\d+$/', trim($chunk))) {
return false;
}
}
return true;
}
public function parse(string $input): int
{
if (! $this->accepts($input)) {
throw new \InvalidArgumentException('Invalid input.');
}
$multiplier = [1, 60, 3600, 86400];
$result = 0;
foreach (array_reverse(explode(':', $input)) as $chunk) {
$value = (int) trim($chunk);
$result += $value * array_shift($multiplier);
}
return $result;
}
}
final class ParserComposite implements TimeStringParser
{
private $parsers;
public function __construct(TimeStringParser ...$parsers)
{
$this->parsers = $parsers;
}
public function accepts(string $input): bool
{
foreach ($this->parsers as $parser) {
if ($parser->accepts($input)) {
return true;
}
}
return false;
}
public function parse(string $input): int
{
foreach ($this->parsers as $parser) {
if ($parser->accepts($input)) {
return $parser->parse($input);
}
}
throw new \InvalidArgumentException('Invalid input.');
}
}
$parser = new ParserComposite(
new InputWithUnits(),
new InputWithoutUnits()
);
$testCases = [
'5d 4h 3m 2s',
'4h 2s',
'3m',
'05: 04: 03: 02',
'4:03:02',
'05: 00: 03: 02',
'02',
'23m',
'2e-5'
];
foreach ($testCases as $testCase) {
if ($parser->accepts($testCase)) {
printf("%-'.20s: %8d\n", $testCase, $parser->parse($testCase));
}
else {
printf("%-'.20s: unsupported\n", $testCase);
}
}
https://3v4l.org/qAYqD

How to return a function inside a function

Make a function has_twenty_ones that returns true if at least one of the players in the game has 21, otherwise return false. This function should use the twenty_ones function.
function has_twenty_ones($game){
function twenty_ones($game)
{
$players_with_score_21 = [];
foreach ($game['players'] as $name => $player) {
$distance = 21 - $player['score'];
if ($distance < 0) {
continue;
}
if ($distance == 21) {
$players_with_score_21 = [$name];
}
}
return $players_with_score_21;
}
return isset($players_with_score_21);
}
what's the best way to code it
Just check if return of twenty_ones function is empty, if it return false overthose return twenty_ones value.
function has_twenty_ones($game){
function twenty_ones($game){
$players_with_score_21 = [];
foreach ($game['players'] as $name => $player) {
$distance = 21 - $player['score'];
if ($distance < 0) {
continue;
}
if ($distance == 21) {
$players_with_score_21 = [$name];
}
}
return $players_with_score_21;
}
$playersWithScore = twenty_ones($game);
if (!empty($playersWithScore)) {
return $playersWithScore;
} else {
return false;
}
}
I'm not sure why you need two functions for this.
As was mentioned by #RiggsFolly, you're not actually making a call to twenty_ones() function. Why not have the following code:
function has_twenty_ones($game)
{
foreach($game['players'] as $name => $player)
{
$distance = 21 - $player['score'];
if ($distance < 0) {
continue;
}
// If at least one player has 21, return true.
if($distance == 21) {
return true;
}
}
return false;
}
The above will return true when it encounters a player who has a score of 21, otherwise it'll return false.
function twenty_ones($game)
{
$players_with_score_21 = [];
foreach ($game['players'] as $name => $player) {
$distance = 21 - $player['score'];
if ($distance < 0) {
continue;
}
if ($distance == 21) {
$players_with_score_21 = [$name];
}
}
return $players_with_score_21;
}
function has_twenty_ones($game){
if (count ($this->twenty_ones($game)) > 0 )
return true;
else
return false;
}

Using unique random numbers for a switch statement

I need a function or an array that gives me random numbers from 1 - 47, so I can use it for my code below.
public function output($question_id, $solution) {
$yes = ($GLOBALS['TSFE']->sys_language_uid == 0) ? 'Ja' : 'Oui';
$no = ($GLOBALS['TSFE']->sys_language_uid == 0) ? 'Nein' : 'No';
switch ($question_id) {
case 1:
$arg = array(
'main_content' => $this->build_html(1, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
break;
case 2:
$arg = array(
'main_content' => $this->build_html(2, '02_0342_05_14_Psori_Arthropathie.jpg', $yes, $no, $solution),
);
break;
case 3:
$arg = array(
'main_content' => $this->build_html(3, '03_0255_05_14_Psori_Arthropathie.jpg', $yes, $no, $solution),
);
break;
}
}
Example
This
case 1:
$arg = array(
'main_content' => $this->build_html(1, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
should look somehow like this:
case $random_id:
$arg = array(
'main_content' => $this->build_html($random_id, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
So that means, each case and each first parameter of the function build_html should get a unique random id.
Of course I could use rand() but then it is quite possible that I get duplicated values.
Any help would be appreciated!
Create a class like follows:
class SamplerWithoutReplacement {
private $pool;
public __construct($min,$max) {
$this->pool = range($min,$max);
}
public function next() {
if (!empty($this->pool)) {
$nIndex = array_rand($this->pool);
$value = $this->pool[$nIndex];
unset($this->pool[$nIndex]);
return $value;
}
return null; //Or throw exception, depends on your handling preference
}
}
Use it as:
$sampler = new SamplerWithoutReplacement(1,47);
//do things
$value = $sampler->next();
The $sampler will draw random samples between 1-47 without replacing them in the pool and therefore they'll be unique.
Not tested but I think it should work:
$numbers = [];
function genenerate_nr_not_in_list($list, $min = 1, $max = 47) {
$number = rand($min, $max);
while (false !== array_search($number, $list)) {
$number = rand($min, $max);
}
return $number;
}
for ($i = 0; $i < 47; $i++) {
$numbers[] = genenerate_nr_not_in_list($numbers);
}
With this solution you can generate numbers in each range ($min and $max parameters). But it should be at least as big as the number of values you need ($max).

Show 1k instead of 1,000

function restyle_text($input){
$input = number_format($input);
$input_count = substr_count($input, ',');
if($input_count != '0'){
if($input_count == '1'){
return substr($input, +4).'k';
} else if($input_count == '2'){
return substr($input, +8).'mil';
} else if($input_count == '3'){
return substr($input, +12).'bil';
} else {
return;
}
} else {
return $input;
}
}
This is the code I have, I thought it was working. apparently not.. can someone help since I can't figure this out.
Try this:
http://codepad.viper-7.com/jfa3uK
function restyle_text($input){
$input = number_format($input);
$input_count = substr_count($input, ',');
if($input_count != '0'){
if($input_count == '1'){
return substr($input, 0, -4).'k';
} else if($input_count == '2'){
return substr($input, 0, -8).'mil';
} else if($input_count == '3'){
return substr($input, 0, -12).'bil';
} else {
return;
}
} else {
return $input;
}
}
Basically, I think you're using the substr() wrong.
Here's a generic way to do this that doesn't require you to use number_format or do string parsing:
function formatWithSuffix($input)
{
$suffixes = array('', 'k', 'm', 'g', 't');
$suffixIndex = 0;
while(abs($input) >= 1000 && $suffixIndex < sizeof($suffixes))
{
$suffixIndex++;
$input /= 1000;
}
return (
$input > 0
// precision of 3 decimal places
? floor($input * 1000) / 1000
: ceil($input * 1000) / 1000
)
. $suffixes[$suffixIndex];
}
And here's a demo showing it working correctly for several cases.
I re-wrote the function to use the properties of numbers rather than playing with strings.
That should be faster.
Let me know if I missed any of your requirements:
function restyle_text($input){
$k = pow(10,3);
$mil = pow(10,6);
$bil = pow(10,9);
if ($input >= $bil)
return (int) ($input / $bil).'bil';
else if ($input >= $mil)
return (int) ($input / $mil).'mil';
else if ($input >= $k)
return (int) ($input / $k).'k';
else
return (int) $input;
}
I do not want to spoil the moment... but I think this is a little more simplified.
Just improving #Indranil answer
e.g.
function comp_numb($input){
$input = number_format($input);
$input_count = substr_count($input, ',');
$arr = array(1=>'K','M','B','T');
if(isset($arr[(int)$input_count]))
return substr($input,0,(-1*$input_count)*4).$arr[(int)$input_count];
else return $input;
}
echo comp_numb(1000);
echo '<br />';
echo comp_numb(1000000);
echo '<br />';
echo comp_numb(1000000000);
echo '<br />';
echo comp_numb(1000000000000);
Or you can too use library How it works is here
Juste install composer require stillat/numeral.php and
<?php
require_once __DIR__.'/vendor/autoload.php';
$formatter = new Stillat\Numeral\Numeral;
$formatter->setLanguageManager(new Stillat\Numeral\Languages\LanguageManager);
$formatter->format(1532, '0a,0'); //Affiche 1.5K

Categories