Php string with separator to page list - php

I try to recreate what we see when we printing page on office or adobe.
For example, when you want to print page 1 to 5 you write : 1-5 and if you want to print a page outside you write : 1-5,8
At the moment I explode string by ',' :
1-5 / 8
Then explode each result by '-' and if I've got result I loop from first page to last and create variable with comma :
1,2,3,4,5,8
Finally I explode by ',' and use array unique to erase double value.
It take some times to achieve this especially when there's a lot of '-'.
Maybe someone got a easier solution to so this ?
Thank
Edit :
$pages = "1-4,6-8,14,16,18-20";
$pages_explode = explode(',',$pages);
foreach($pages_explode as $page){
$page_explode = explode('-',$page);
if(!empty($page_explode[1])){
for ($i=$page_explode[0]; $i<=$page_explode[1] ; $i++) {
$page_final .= $i.',';
}
}else{
$page_final .= $page_explode[0].',';
}
}
$page_final = explode(',',$page_final);
$page_final = array_unique ($page_final);
foreach($page_final as $value){
echo $value.'<br>';
}

Is it a code golf challenge?
Well a basic approach seems fine to me :
$input = '1-5,6-12,8';
$patterns = explode(',', $input);
$pages = [];
foreach ($patterns as $pattern) {
if (2 == count($range = explode('-', $pattern))) {
$pages = array_merge($pages, range($range[0], $range[1]));
} else {
$pages[] = (int)$pattern;
}
}
$uniquePages = array_unique($pages);
var_dump($uniquePages);
Outputs :
array (size=12)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
5 => int 6
6 => int 7
7 => int 8
8 => int 9
9 => int 10
10 => int 11
11 => int 12

Having to remove duplicates suggests that you have overlapping ranges in your strings.
Eg: 1-5,2-9,7-15,8,10
You seems to process all these without considering the overlapping areas and finally attempt to remove duplicates by the expensive array_unique function.
Your code should instead remember the minimum and maximum of the resulting range and not process anything that overlaps this range.
Following is a sample code which demonstrates the idea. But its certainly faster than the code you have suggested in your question. You should add parts there to process additional types of delimiters, if any, in your requirement.
<?php
$ranges = "1-5,3-7,6-10,8,11";
$min = 10000;
$max = -1;
$numbers = array(); //Your numbers go here
//Just a utility function to generate numbers from any range and update margins
function generateNumbers($minVal, $maxVal) {
global $min, $max, $numbers;
for ($i = $minVal; $i <= $maxVal; $i++) {
array_push($numbers, $i);
if ($i < $min)
$min = $i;
if ($i > $max)
$max = $i;
}
}
//Seperate ranges
$sets = explode(",", $ranges);
//Go through each range
foreach($sets as $aSet) {
//Extract the range or get individual numbers
$range = explode("-", $aSet);
if (count($range) == 1) { //its an individual number. So check margins and insert
$aSet = intval($aSet);
if ($aSet < $min){
array_push($numbers, $aSet);
$min = $aSet;
}
if ($aSet > $max){
array_push($numbers, $aSet);
$max = $aSet;
}
continue; // <----- For single numbers it ends here
}
//Its a range
$rangeLow = intval($range[0]);
$rangeHigh = intval($range[1]);
//Adjusting numbers to omit cases when ranges fall right on the margins
if ($rangeLow == $min){
$rangeLow++;
}
else
if ($rangeLow == $max) {
$rangeLow--;
}
if ($rangeHigh == $min){
$rangeHigh++;
}
else
if ($rangeHigh == $max) {
$rangeHigh--;
}
//Check if below or above the generated range
if (($rangeLow < $min && $rangeHigh < $min) || ($rangeLow > $max && $rangeHigh > $max)) {
generateNumbers($rangeLow, $rangeHigh);
};
//Check if across the lower edge of the generated range
if ($rangeLow < $min && $rangeHigh > $min && $rangeHigh < $max) {
generateNumbers($rangeLow, $min - 1);
};
//Check if across the upper edge of the generated range
if ($rangeLow > $min && $rangeLow < $max && $rangeHigh > $max) {
generateNumbers($max + 1, $rangeHigh);
};
}
//Now just sort the array
print_r($numbers);
?>

Related

Generate List of Unique Four-Digit Numbers Without Repeating Digits and Without Forward-Sequential Digits

I had a need to generate a list of four-digit numbers for use as codes. The digits should not repeat, and each next digit should not be sequential. There were some questions that were similar but not enough for me to answer. I chose to share my function instead. It did not matter if reverse numbers were in the list e.g. 1357 > 7531.
It occurred to me that it there may be an opportunity for a recursive function, possibly to return five or six-digit numbers. Improvements to my function are most welcome.
public function codeList() {
$data = [];
for ($ii=0; $ii < 10; $ii++) {
for ($jj=0; $jj < 10; $jj++) {
for ($kk=0; $kk < 10; $kk++) {
for ($ll=0; $ll < 10; $ll++) {
$str = "{$ii}{$jj}{$kk}{$ll}";
$arr = str_split($str);
if (count($arr) === count(array_unique($arr))) {
if (($arr[0] + 1 != $arr[1]) && ($arr[1] + 1 != $arr[2]) && ($arr[2] + 1 != $arr[3])) {
$data[] = $str;
}
}
}
}
}
}
return $data;
} # END FUNCTION codeList

How can I generate and validate random IDs using the Saudi ID format?

I need to generate random IDs that validate against the criteria for Saudi IDs shown in this question:
Saudi Iqama/National Identity number field validation
I've tried the following code:
$random_numbers = [];
while(count($random_numbers) < 1000000000){
do {
$random_number = mt_rand(1000000000,9000000000);
}
while (in_array($random_number, $random_numbers));{
$type = substr ( $random_number, 0, 1 );
if($type != 2 && $type != 1 ) break;
$sum = 0;
for( $i = 0 ; $i<10 ; $i++ ) {
if ( $i % 2 == 0){
$ZFOdd = str_pad ( ( substr($random_number, $i, 1) * 2 ), 2, "0", STR_PAD_LEFT );
$sum += substr ( $ZFOdd, 0, 1 ) + substr ( $ZFOdd, 1, 1 );
}else{
$sum += substr ( $random_number, $i, 1 );
}
}
return $sum%10 ? break : echo $random_number;
----------
echo "<br>";
$random_numbers[] = $random_number;}
}
Disclaimer: I'm not 100% sure on the validation required etc. for Saudi ID numbers and have only briefly looked at the answers supplied in the linked question
Okay, so, my understanding is that you need to generate a random id that:
Matches the pattern/format:
[12]\d{9}
Validates against the criteria show in the linked question:
Saudi Iqama/National Identity number field validation
To do this we need to create a couple of functions; one to generate IDs and one to validate the IDs against the given criteria.
Generate the ID
Simply generating an ID is simple enough. We can use the random_int function in PHP with a loop. If we enclose the code to generate the ID inside of a do...while... loop then we can execute the code and validate the ID repeatedly until we get a valid one.
function getRandomSaudiId() : int
{
do {
$saudiId = (string) random_int(1,2);
for($i = 0; $i < 9; $i++){
$saudiId .= random_int(0,9);
}
} while(validateSaudiId($saudiId) === false);
return (int) $saudiId;
}
Validate the ID
Note: we convert to string so that we can access the numbers based on their index.
function validateSaudiId(string $id) : bool
{
$sum = 0;
for($i = 0; $i < 9; $i++){
if( $i % 2 ){
// Even number
$sum += $id[$i];
}
else{
//Odd number
$increment = $id[$i] * 2;
while($increment > 9){
$increment = (string) $increment;
$increment = $increment[0] + $increment[1];
}
$sum += $increment;
}
}
$sum = (string) $sum;
return ($sum[1] == $id[9] || $id[9] == (10 - $sum[1])) ? true : false;
}
Example use
for($i = 0; $i < 10; $i++) var_dump(getRandomSaudiId());
/*
Output:
int(2933617506)
int(2409806096)
int(1072585118)
int(2891306413)
int(1810304558)
int(2591965856)
int(1363032527)
int(1031823269)
int(1265954048)
int(2498099472)
int(1134172537)
*/

Get lowest price on sum of combinations in given array

This code is working fine when the array length is 8 or 10 only. When we are checking this same code for more than 10 array length.it get loading not showing the results.
How do reduce my code. If you have algorithm please share. Please help me.
This program working flow:
$allowed_per_room_accommodation =[2,3,6,5,3,5,2,5,4];
$allowed_per_room_price =[10,30,60,40,30,50,20,60,80];
$search_accommodation = 10;
i am get subsets = [5,5],[5,3,2],[6,4],[6,2,2],[5,2,3],[3,2,5]
Show lowest price room and then equal of 10 accommodation; output like as [5,3,2];
<?php
$dp=array(array());
$GLOBALS['final']=[];
$GLOBALS['room_key']=[];
function display($v,$room_key)
{
$GLOBALS['final'][] = $v;
$GLOBALS['room_key'][] = $room_key;
}
function printSubsetsRec($arr, $i, $sum, $p,$dp,$room_key='')
{
// If we reached end and sum is non-zero. We print
// p[] only if arr[0] is equal to sun OR dp[0][sum]
// is true.
if ($i == 0 && $sum != 0 && $dp[0][$sum]) {
array_push($p,$arr[$i]);
array_push($room_key,$i);
display($p,$room_key);
return $p;
}
// If $sum becomes 0
if ($i == 0 && $sum == 0) {
display($p,$room_key);
return $p;
}
// If given sum can be achieved after ignoring
// current element.
if (isset($dp[$i-1][$sum])) {
// Create a new vector to store path
// if(!is_array(#$b))
// $b = array();
$b = $p;
printSubsetsRec($arr, $i-1, $sum, $b,$dp,$room_key);
}
// If given $sum can be achieved after considering
// current element.
if ($sum >= $arr[$i] && isset($dp[$i-1][$sum-$arr[$i]]))
{
if(!is_array($p))
$p = array();
if(!is_array($room_key))
$room_key = array();
array_push($p,$arr[$i]);
array_push($room_key,$i);
printSubsetsRec($arr, $i-1, $sum-$arr[$i], $p,$dp,$room_key);
}
}
// Prints all subsets of arr[0..n-1] with sum 0.
function printAllSubsets($arr, $n, $sum,$get=[])
{
if ($n == 0 || $sum < 0)
return;
// Sum 0 can always be achieved with 0 elements
// $dp = new bool*[$n];
$dp = array();
for ($i=0; $i<$n; ++$i)
{
// $dp[$i][$sum + 1]=true;
$dp[$i][0] = true;
}
// Sum arr[0] can be achieved with single element
if ($arr[0] <= $sum)
$dp[0][$arr[0]] = true;
// Fill rest of the entries in dp[][]
for ($i = 1; $i < $n; ++$i) {
for ($j = 0; $j < $sum + 1; ++$j) {
// echo $i.'d'.$j.'.ds';
$dp[$i][$j] = ($arr[$i] <= $j) ? (isset($dp[$i-1][$j])?$dp[$i-1][$j]:false) | (isset($dp[$i-1][$j-$arr[$i]])?($dp[$i-1][$j-$arr[$i]]):false) : (isset($dp[$i - 1][$j])?($dp[$i - 1][$j]):false);
}
}
if (isset($dp[$n-1][$sum]) == false) {
return "There are no subsets with";
}
$p;
printSubsetsRec($arr, $n-1, $sum, $p='',$dp);
}
$blockSize = array('2','3','6','5','3','5','2','5','4');
$blockvalue = array('10','30','60','40','30','50','20','60','80');
$blockname = array("map","compass","water","sandwich","glucose","tin","banana","apple","cheese");
$processSize = 10;
$m = count($blockSize);
$n = count($processSize);
// sum of sets in array
printAllSubsets($blockSize, $m, $processSize);
$final_subset_room = '';
$final_set_room_keys = '';
$final_set_room =[];
if($GLOBALS['room_key']){
foreach ($GLOBALS['room_key'] as $set_rooms_key => $set_rooms) {
$tot = 0;
foreach ($set_rooms as $set_rooms) {
$tot += $blockvalue[$set_rooms];
}
$final_set_room[$set_rooms_key] = $tot;
}
asort($final_set_room);
$final_set_room_first_key = key($final_set_room);
$final_all_room['set_room_keys'] = $GLOBALS['room_key'][$final_set_room_first_key];
$final_all_room_price['set_room_price'] = $final_set_room[$final_set_room_first_key];
}
if(isset($final_all_room_price)){
asort($final_all_room_price);
$final_all_room_first_key = key($final_all_room_price);
foreach ($final_all_room['set_room_keys'] as $key_room) {
echo $blockname[$key_room].'---'. $blockvalue[$key_room];
echo '<br>';
}
}
else
echo 'No Results';
?>
I'm assuming your task is, given a list rooms, each with the amount of people it can accommodate and the price, to accommodate 10 people (or any other quantity).
This problem is similar to 0-1 knapsack problem which is solvable in polynomial time. In knapsack problem one aims to maximize the price, here we aim to minimize it. Another thing that is different from classic knapsack problem is that full room cost is charged even if the room is not completely occupied. It may reduce the effectiveness of the algorithm proposed at Wikipedia. Anyway, the implementation isn't going to be straightforward if you have never worked with dynamic programming before.
If you want to know more, CLRS book on algorithms discusses dynamic programming in Chapter 15, and knapsack problem in Chapter 16. In the latter chapter they also prove that 0-1 knapsack problem doesn't have trivial greedy solution.

How to convert word to number using my function?

I created this function to converting numbers to words. And how I can convert words to number using this my function:
Simple function code:
$array = array("1"=>"ЯК","2"=>"ДУ","3"=>"СЕ","4"=>"ЧОР","5"=>"ПАНҶ","6"=>"ШАШ","7"=>"ҲАФТ","8"=>"ХАШТ","9"=>"НӮҲ","0"=>"НОЛ","10"=>"ДАҲ","20"=>"БИСТ","30"=>"СИ","40"=>"ЧИЛ","50"=>"ПАНҶОҲ","60"=>"ШАСТ","70"=>"ҲАФТОД","80"=>"ХАШТОД","90"=>"НАВАД","100"=>"САД");
$n = "98"; // Input number to converting
if($n < 10 && $n > -1){
echo $array[$n];
}
if($n == 10 OR $n == 20 OR $n == 30 OR $n == 40 OR $n == 50 OR $n == 60 OR $n == 70 OR $n == 80 OR $n == 90 OR $n == 100){
echo $array[$n];
}
if(mb_strlen($n) == 2 && $n[1] != 0)
{
$d = $n[0]."0";
echo "$array[$d]У ".$array[$n[1]];
}
My function so far converts the number to one hundred. How can I now convert text to a number using the answer of my function?
So, as #WillParky93 assumed, your input has spaces between words.
<?php
mb_internal_encoding("UTF-8");//For testing purposes
$array = array("1"=>"ЯК","2"=>"ДУ","3"=>"СЕ","4"=>"ЧОР","5"=>"ПАНҶ","6"=>"ШАШ","7"=>"ҲАФТ","8"=>"ХАШТ","9"=>"НӮҲ","0"=>"НОЛ","10"=>"ДАҲ","20"=>"БИСТ","30"=>"СИ","40"=>"ЧИЛ","50"=>"ПАНҶОҲ","60"=>"ШАСТ","70"=>"ҲАФТОД","80"=>"ХАШТОД","90"=>"НАВАД","100"=>"САД");
$postfixes = array("3" => "ВУ");
$n = "98"; // Input number to converting
$res = "";
//I also optimized your conversion of numbers to words
if($n > 0 && ($n < 10 || $n%10 == 0))
{
$res = $array[$n];
}
if($n > 10 && $n < 100 && $n%10 != 0)
{
$d = intval(($n/10));
$sd = $n%10;
$ending = isset($postfixes[$d]) ? $postfixes[$d] : "У";
$res = ($array[$d * 10]).$ending." ".$array[$sd];
}
echo $res;
echo "\n<br/>";
$splitted = explode(" ", $res);
//According to your example, you use only numerals that less than 100
//So, to simplify your task(btw, according to Google, the language is tajik
//and I don't know the rules of building numerals in this language)
if(sizeof($splitted) == 1) {
echo array_search($splitted[0], $array);
}
else if(sizeof($splitted) == 2) {
$first = $splitted[0];
$first_length = mb_strlen($first);
if(mb_substr($first, $first_length - 2) == "ВУ")
{
$first = mb_substr($first, 0, $first_length - 2);
}
else
{
$first = mb_substr($splitted[0], 0, $first_length - 1);
}
$second = $splitted[1];
echo (array_search($first, $array) + array_search($second, $array));
}
You didn't specify the input specs but I took the assumption you want it with a space between the words.
//get our input=>"522"
$input = "ПАНҶ САД БИСТ ДУ";
//split it up
$split = explode(" ", $input);
//start out output
$c = 0;
//set history
$history = "";
//loop the words
foreach($split as &$s){
$res = search($s);
//If number is 9 or less, we are going to check if it's with a number
//bigger than or equal to 100, if it is. We multiply them together
//else, we just add them.
if((($res = search($s)) <=9) ){
//get the next number in the array
$next = next($split);
//if the number is >100. set $nextres
if( ($nextres = search($next)) >= 100){
//I.E. $c = 5 * 100 = 500
$c = $nextres * $res;
//set the history so we skip over it next run
$history = $next;
}else{
//Single digit on its own
$c += $res;
}
}elseif($s != $history){
$c += $res;
}
}
//output the result
echo $c;
function search($s){
global $array;
if(!$res = array_search($s, $array)){
//grab the string length
$max = strlen($s);
//remove one character at a time until we find a match
for($i=0;$i<$max; $i++ ){
if($res = array_search(mb_substr($s, 0, -$i),$array)){
//stop the loop
$i = $max;
}
}
}
return $res;
}
Output is 522.

IMEI validation function

Does anybody know a PHP function for IMEI validation?
Short solution
You can use this (witchcraft!) solution, and simply check the string length:
function is_luhn($n) {
$str = '';
foreach (str_split(strrev((string) $n)) as $i => $d) {
$str .= $i %2 !== 0 ? $d * 2 : $d;
}
return array_sum(str_split($str)) % 10 === 0;
}
function is_imei($n){
return is_luhn($n) && strlen($n) == 15;
}
Detailed solution
Here's my original function that explains each step:
function is_imei($imei){
// Should be 15 digits
if(strlen($imei) != 15 || !ctype_digit($imei))
return false;
// Get digits
$digits = str_split($imei);
// Remove last digit, and store it
$imei_last = array_pop($digits);
// Create log
$log = array();
// Loop through digits
foreach($digits as $key => $n){
// If key is odd, then count is even
if($key & 1){
// Get double digits
$double = str_split($n * 2);
// Sum double digits
$n = array_sum($double);
}
// Append log
$log[] = $n;
}
// Sum log & multiply by 9
$sum = array_sum($log) * 9;
// Compare the last digit with $imei_last
return substr($sum, -1) == $imei_last;
}
Maybe can help you :
This IMEI number is something like this: ABCDEF-GH-IJKLMNO-X (without “-” characters)
For example: 350077523237513
In our example ABCDEF-GH-IJKLMNO-X:
AB is Reporting Body Identifier such as 35 = “British Approvals Board of Telecommunications (BABT)”
ABCDEF is Type Approval Code
GH is Final Assembly Code
IJKLMNO is Serial Number
X is Check Digit
Also this can help you : http://en.wikipedia.org/wiki/IMEI#Check_digit_computation
If i don't misunderstood, IMEI numbers using Luhn algorithm . So you can google this :) Or you can search IMEI algorithm
Maybe your good with the imei validator in the comments here:
http://www.php.net/manual/en/function.ctype-digit.php#77718
But I haven't tested it
Check this solution
<?php
function validate_imei($imei)
{
if (!preg_match('/^[0-9]{15}$/', $imei)) return false;
$sum = 0;
for ($i = 0; $i < 14; $i++)
{
$num = $imei[$i];
if (($i % 2) != 0)
{
$num = $imei[$i] * 2;
if ($num > 9)
{
$num = (string) $num;
$num = $num[0] + $num[1];
}
}
$sum += $num;
}
if ((($sum + $imei[14]) % 10) != 0) return false;
return true;
}
$imei = '868932036356090';
var_dump(validate_imei($imei));
?>
IMEI validation uses Luhn check algorithm. I found a link to a page where you can validate your IMEI. Furthermore, at the bottom of this page is a piece of code written in JavaScript to show how to calculate the 15th digit of IMEI and to valid IMEI. I might give you some ideas. You can check it out here http://imei.sms.eu.sk/index.html
Here is a jQuery solution which may be of use: https://github.com/madeinstefano/imei-validator
good fun from kasperhartwich
function validateImei($imei, $use_checksum = true) {
if (is_string($imei)) {
if (ereg('^[0-9]{15}$', $imei)) {
if (!$use_checksum) return true;
for ($i = 0, $sum = 0; $i < 14; $i++) {
$tmp = $imei[$i] * (($i%2) + 1 );
$sum += ($tmp%10) + intval($tmp/10);
}
return (((10 - ($sum%10)) %10) == $imei[14]);
}
}
return false;
}

Categories