How do i calculate characters in string without if conditions? - php

I have the code example:
$str = '123456789101112131415';
if(strlen($str) <9 )
{
echo "the string is 10";
}
elseif(strlen($str) > 9) {
echo 'the string is 11';
}
elseif(strlen($str) > 21) {
echo 'the string is 30';
}
elseif(strlen($str) > 31) {
echo 'the string is 40';
}
My problem is how do I sort out when the string is huge and not specified in the if conditions

Calculate it!
call strlen()
divide by 10
call floor() to remove decimals
multiply by 10
check if less than 10
Here is a battery of tests:
Code: (Demo)
$strings=[9=>'123456789',10=>'1234567890',11=>'12345678901',19=>'1234567890123456789',20=>'12345678901234567890',21=>'123456789012345678901'];
foreach($strings as $k=>$str){
if(($group=floor(strlen($str)/10)*10)<10){ // use arithmetic to find the group, declare and check group in one step
echo "$k => $str is not upto 10";
}else{
echo "$k => $str is in group $group";
}
echo "\n";
}
Output:
9 => 123456789 is not upto 10
10 => 1234567890 is in group 10
11 => 12345678901 is in group 10
19 => 1234567890123456789 is in group 10
20 => 12345678901234567890 is in group 20
21 => 123456789012345678901 is in group 20
After OP's update:
Code: (Demo)
$strings=[9=>'123456789',10=>'1234567890',11=>'12345678901',19=>'1234567890123456789',20=>'12345678901234567890',21=>'123456789012345678901'];
foreach($strings as $k=>$str){
echo "$k => $str is in group ",(floor(strlen($str)/10)+1)*10,"\n";
}
Output:
9 => 123456789 is in group 10
10 => 1234567890 is in group 20
11 => 12345678901 is in group 20
19 => 1234567890123456789 is in group 20
20 => 12345678901234567890 is in group 30
21 => 123456789012345678901 is in group 30
Here is another method that delivers EXACTLY what the OP is asking for, but I bet it's "not right" either.
http://sandbox.onlinephpfunctions.com/code/77effe7f68c389bafb1d104a49b7055873e7b038

Related

Exclude text to valid array

i have a text and i want convert it to array by exclude but i cant get true array
# SRC-ADDRESS DST-ADDRESS PACKETS BYTES SRC-USER DST-USER 0 10.40.47.48 216.58.205.211 12 822 2 1 10.40.47.48 102.132.97.21 66 9390 2 2 184.106.10.77 10.40.47.252 10 1819 1 3 10.40.47.252 104.27.155.225 1 41 1 4 10.40.47.252 144.76.103.6 5 878 1 5 102.132.97.35 10.40.47.252 11 1159 1 6 10.40.47.252 52.169.53.217 1 397 1 7 104.27.155.225 10.40.47.252 1 52 1
and i want result like this
Array
(
[0] => Array
(
[.id] => *0
[src-address] => 10.40.47.50
[dst-address] => 185.144.157.141
[packets] => 6
[bytes] => 1349
)
[1] => Array
(
[.id] => *1
[src-address] => 195.122.177.151
[dst-address] => 10.40.47.48
[packets] => 4
[bytes] => 174
[dst-user] => 2
)
....
i try this but it is wrong
$arr = exclude(" ",$text);
edit :
i can get text by another way
0 src-address=108.177.15.188 dst-address=10.40.47.252 packets=1 bytes=52 dst-user="1" 1 src-address=10.40.47.48 dst-address=172.217.19.150 packets=11 bytes=789 src-user="2" 2 src-address=184.106.10.77 dst-address=10.40.47.252 packets=26 bytes=5450 dst-user="1"
As I mentioned in the comments, one way would be to first explode your input by " " (space). You loop through each element/row of the resulting array. Then you explode each of those by = (equals sign). If the result of that explode is a single-element array, you know you should start a new row and create a key-value pair using your special .id key. If the count of the result is two, take the first part and make it the key of a new key-value pair in the current row, and take the second part and make it the value of that key-value pair.
There's a bit of a wrinkle in the fact that some of your source values are quoted, but you seem to want them not quoted in the result. To handle that we do a lose equality check on the value to see if it is the same when converted to an integer or not. If it is, then we convert it to remove the quotes.
$inputText = '0 src-address=108.177.15.188 dst-address=10.40.47.252 packets=1 bytes=52 dst-user="1" 1 src-address=10.40.47.48 dst-address=172.217.19.150 packets=11 bytes=789 src-user="2" 2 src-address=184.106.10.77 dst-address=10.40.47.252 packets=26 bytes=5450 dst-user="1"';
$result = array();
$spaceParts = explode(" ", $inputText);
foreach($spaceParts as $part)
{
$subParts = explode("=", $part);
if(count($subParts) == 1)
{
$resultIndex = (isset($resultIndex) ? $resultIndex+1 : 0);
$result[$resultIndex] = array(".id" => "*".$part);
}
else if(count($subParts) == 2)
{
$result[$resultIndex][$subParts[0]] = ((int)$subParts[1] == $subParts[1] ? (int)$subParts[1] : $subParts[1]);
}
else
{
// unexpected, handle however you want
}
}
print_r($result);
DEMO
Try reading the string in using str_getcsv replacing the delimiter with whatever the string is delimited by.
var_dump(str_getcsv($input, ","));
Note the manual states that the delimiter must be one char long. If wanting a tab or multiple spaces you will need to look into the answer:
str_getcsv on a tab-separated file
str-getcsv php manual
Here is something that could work but I would recoment using the csv methods instead to read the data in . And it is unclear how your data should be actually mapped to header.
$header = "# SRC-ADDRESS DST-ADDRESS PACKETS BYTES SRC-USER DST-USER ";
$input = "# SRC-ADDRESS DST-ADDRESS PACKETS BYTES SRC-USER DST-USER 0 10.40.47.48 216.58.205.211 12 822 2 1 10.40.47.48 102.132.97.21 66 9390 2 2 184.106.10.77 10.40.47.252 10 1819 1 3 10.40.47.252 104.27.155.225 1 41 1 4 10.40.47.252 144.76.103.6 5 878 1 5 102.132.97.35 10.40.47.252 11 1159 1 6 10.40.47.252 52.169.53.217 1 397 1 7 104.27.155.225 10.40.47.252 1 52 1 ";
$string = str_replace($header, "", $input );
$delimiter = " ";
$columns = 6;
$splitData = explode($delimiter, $string);
$result = [];
$i= 0;
foreach ($splitData as $key => $value) {
$result[$i][] = $value;
if (($key+1) % $columns == 0 ){
$i++;
}
}
var_dump($result);
Using the second example with the 0 src-address=108.177.15.188 dst-address=10.40.47.252 packets=1 bytes=52 dst-user="1" format, there are 6 entries:
$result = array_map(function($v) {
parse_str("id=".implode("&", $v), $output);
return $output;
}, array_chunk(explode(' ', $text), 6));
Explode the array on spaces
Chunk the array into 6 entries per element
Map to a function that implodes each array on & and parse it as a query string

find the occurrence of every character and word in ascending order -php

I am trying to make an application which will take an input string and in return application finds the occurrence of every character in ascending order as well as the occurrence of words also in ascending order.
I am confused why its showing 18 times in letter count and how to sort it .
HTML
<form method="POST" action="counter.php">
<textarea rows="4" cols="50" name="string">
</textarea>
<input type="submit" value="submit">
</form>
PHP
<?php
if (isset($_POST['string'])) {
$string=$_POST['string'];
echo("Counting Words").'<br>';
print_r(array_count_values(str_word_count($string,1)));
echo '<br>';
echo("Letter Count").'<br>';
$chart = count_chars($string, 1);
echo '<br>';
foreach($chart as $letter=>$frequency)
echo chr($letter)." $frequency times<br />";
}
?>
OUTPUT:
Counting Words
Array ( [A] => 1 [string] => 1 [with] => 1 [certain] => 1 [words] => 2 [occuring] => 1 [more] => 1 [often] => 1 [than] => 1 [other] => 1 )
Letter Count
18 times
A 1 times
a 2 times
c 3 times
d 2 times
e 4 times
f 1 times
g 2 times
h 3 times
i 4 times
m 1 times
n 5 times
o 6 times
r 7 times
s 3 times
t 6 times
u 1 times
w 3 times
There is a lot of built-in php function, try to check them out first. One of them you can use is called substring count.(http://php.net/manual/en/function.substr-count.php).
<?php
$text = 'This is a test';
echo strlen($text); // 14
echo substr_count($text, 'is'); // 2
// the string is reduced to 's is a test', so it prints 1
echo substr_count($text, 'is', 3);
// the text is reduced to 's i', so it prints 0
echo substr_count($text, 'is', 3, 3);
// generates a warning because 5+10 > 14
echo substr_count($text, 'is', 5, 10);
You just need to check for each letter and occurance and repeating it in a set of array, to find each character occurence.

Avoiding loops in a complex calculation with arrays

I have two arrays of numbers, one containing a lot of numbers, one only a few. There are no duplicates within or between arrays:
$all = range(1, 50);
$few = array(7, 11, 19, 27, 29, 36, 40, 43);
$many = array_merge(array_diff($all, $few));
I now want to calculate the differences between each of the "few" numbers and all of the "many" that follow it but come before the next of the "few". For example, among $many, only 28 falls between 27 and 29 from $few, so I want to calculate the difference between 28 and 27. No other differences to 27 are calculated, because no other $many fall between 27 and 29. For 19 from $few I will calculate the differences to 20, 21, 22, 23, 24, 25 and 26, because they all lie between 19 and the next number from $few, which is 27.
To calculate the differences, I use loops. Here is a somewhat simplified code (which ignores the fact that there is no index [$i + 1] for the last number in $few):
$differences = array();
for($i = 0; $i < count($few); $i++) {
foreach($many as $m) {
if($m > $few[$i] && $m < $few[$i + 1]) {
$differences[] = $m - $few[$i];
}
}
}
If I have huge arrays, the loops will take a long time to run. So:
Is there a better way to calculate the differences, without using loops?
The resulting $differences looks like this:
Array $many $few
( ↓ ↓
[0] => 1 // 8 - 7 = 1
[1] => 2 // 9 - 7
[2] => 3 // 10 - 7
[3] => 1 // 12 - 11
[4] => 2
[5] => 3
[6] => 4
[7] => 5
[8] => 6
[9] => 7
[10] => 1
[11] => 2
[12] => 3
[13] => 4
[14] => 5
[15] => 6
[16] => 7
[17] => 1
[18] => 1
[19] => 2
[20] => 3
[21] => 4
[22] => 5
[23] => 6
[24] => 1
[25] => 2
[26] => 3
[27] => 1
[28] => 2
)
My basic reasoning is that as a human, I don't see two arrays that I compare:
... 16 17 18 | 20 21 22 23 24 25 26 | 28 29 30 31 ...
exclude | include | exclude
19 (27)
But rather one number line that I go along from one number to the next, and when I meet one marked "few" I will calculate all the differences to each of the following numbers, until I meet another one marked "few":
... 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ...
... m m m f m m m m m m m f m m m m ...
↑ ↑ ... ↑
start calculate stop
Because it is sorted, I don't have to go over the whole $many-array number by number for every number from $few. So can we somehow take the fact into account that the arrays are ordered? Or maybe build one array that contains the markers ("f", "m") and the numbers as keys? E.g.:
$all = array("drop this", "m", "m", "m", "m", "m", "m", "f", ...);
unset($all[0]); // drops the first element with index 0
Apart from the two calls to sort(), all you need is a single loop through $many.
// Input data provided in the question
$all = range(1, 50);
$few = array(7, 11, 19, 27, 29, 36, 40, 43);
$many = array_values(array_diff($all, $few));
// Display the values to see what we are doing
echo('$few = ['.implode(' ', $few)."]\n");
echo('$many = ['.implode(' ', $many)."]\n");
//
// The actual algorithm starts here
// Sort both $few and $many
// it works fast enough and it is required for the rest of the algorithm
sort($few);
sort($many);
// Be sure the last value of $few is larger than the last value of $many
// This is needed to avoid extra checking for the last element of $few inside the loop
if (end($few) < end($many)) {
array_push($few, end($many) + 1);
}
// Extract the first two items from $few
$current = array_shift($few);
$next = array_shift($few);
// This is the result
$differences = array();
// Run only once through $many, check each item against $next
// subtract $current from it; advance when $next was reached
foreach ($many as $item) {
// Skip the items smaller than the first element from $few
if ($item < $current) {
continue;
}
// If the next element from $few was reached then advance to the next interval
while ($next < $item) {
$current = $next;
$next = array_shift($few);
}
// Here $current < $item < $next
// This echo() is for debug purposes
echo('$current = '.$current.'; $item = '.$item.'; $next = '.$next.'; difference='.($item - $current)."\n");
// Store the difference
$differences[] = $item - $current;
}

String number approximation

I was thinking about a "number approximation" function that takes an integer and returns a string, similar to the following:
45 => "some"
100 => "1 hundred"
150 => "over 1 hundred"
1,386 => "over 1 thousand"
15,235,742 => "over 15 million"
797,356,264,255 => "over 700 billion"
I was hoping to use it for, for example, saying how many rows in a database table in an approximate manner.
I couldn't think how to describe such a thing so searching for it has been somewhat tricky.
Does any body know of an existing function (preferably in PHP) that does this, or could anybody describe/point to an algo to get me started on rolling my own?
Take a look at this package: http://pear.php.net/package-info.php?package=Numbers_Words
The following code explained in comments would do it
I've given two options. One only with words. The second that one you have exactly said in your answer. The first one is easier because you don't need to preconvert words to numbers again.
<?php
require_once "Numbers/Words.php";
$number = new Numbers_Words();
$input = "797,356,264,255";
$input = str_replace(',', '',$input); // removing the comas
$output = $input[0]; // take first char (7)
$output2 = $input[0].'00'; //7 + appended 00 = 700 (for displaying 700 instead of 'seven hundred')
for ($i = 1; $i<strlen($input); $i++) {
$output .= '0';
}
$words = $number->toWords($output); //seven hundred billion
$output3 = explode(' ', $words);
$word = $output3[count($output3)-1]; // billion
echo "Over ". $words; // Over seven hundred billion
#####################
echo "Over " . $output2 . ' ' . $word; // Over 700 billion
What do you want to do is so subjective. That's why you cannot find any function to do that.
For your algorithm, you can define some strings which will match with patterns. For example: over ** million matches with a number of 8 digits. You can find the firsts 2 digits and replace ** in the string.
Then you can use math function like round, floor, ceil (it depends of what you want), and find the string corresponding to your pattern.
After a little bit of fiddling I have come up with this:
function numberEstimate($number) {
// Check for some special cases.
if ($number < 1) {
return "zero";
} else if ($number< 1000) {
return "less than 1 thousand";
}
// Define the string suffixes.
$sz = array("thousand", "million", "billion", "trillion", "gazillion");
// Calculate.
$factor = floor((strlen($number) - 1) / 3);
$number = floor(($number / pow(1000, $factor)));
$number = floor(($number / pow(10, strlen($number) - 1))) * pow(10, strlen($number) - 1);
return "over ".$number." ".#$sz[$factor - 1];
}
which outputs something like this:
0 => "zero"
1 => "less than 1 thousand"
10 => "less than 1 thousand"
11 => "less than 1 thousand"
56 => "less than 1 thousand"
99 => "less than 1 thousand"
100 => "less than 1 thousand"
101 => "less than 1 thousand"
465 => "less than 1 thousand"
890 => "less than 1 thousand"
999 => "less than 1 thousand"
1,000 => "over 1 thousand"
1,001 => "over 1 thousand"
1,956 => "over 1 thousand"
56,123 => "over 50 thousand"
99,213 => "over 90 thousand"
168,000 => "over 100 thousand"
796,274 => "over 700 thousand"
999,999 => "over 900 thousand"
1,000,000 => "over 1 million"
1,000,001 => "over 1 million"
5,683,886 => "over 5 million"
56,973,083 => "over 50 million"
964,289,851 => "over 900 million"
769,767,890,753 => "over 700 billion"
7,687,647,652,973,863 => "over 7 gazillion"
It may not be the prettiest solution, or the most elegant, but it seems to work and does a good job so I will probably go along with this.
I thank everyone for the pointers and suggestions!

Split 4 digit numbers

I want to split a 4 digit number with 4 digit decimal .
Inputs:
Input 1 : 5546.263
Input 2 : 03739.712 /*(some time may have one zero at first)*/
Result: (array)
Result of input 1 : 0 => 55 , 1 => 46.263
Result of input 2 : 0 => 37 , 1 => 39.712
P.S : Inputs is GPS data and always have 4 digit as number / 3 digit as decimal and some time have zero at first .
You could use the following function:
function splitNum($num) {
$num = ltrim($num, '0');
$part1 = substr($num, 0, 2);
$part2 = substr($num, 2);
return array($part1, $part2);
}
Test case 1:
print_r( splitNum('5546.263') );
Output:
Array
(
[0] => 55
[1] => 46.263
)
Test case 2:
print_r( splitNum('03739.712') );
Output:
Array
(
[0] => 37
[1] => 39.712
)
Demo!
^0*([0-9]{2})([0-9\.]+) should work just fine and do what you want:
$input = '03739.712';
if (preg_match('/^0*([0-9]{2})([0-9\.]+)/', $input, $matches)) {
$result = array((int)$matches[1], (float)$matches[2]);
}
var_dump($result); //array(2) { [0]=> int(37) [1]=> float(39.712) }
Regex autopsy:
^ - the string MUST start here
0* - the character '0' repeated 0 or more times
([0-9]{2}) - a capturing group matching a digit between 0 and 9 repeated exactly 2 times
([0-9\.]+) - a capturing group matching a digit between 0 and 9 OR a period repeated 1 or more times
Optionally you can add $ to the end to specify that "the string MUST end here"
Note: Since we cast to an int in the first match, you can omit the 0* part, but if you plan NOT to cast it, then leave it in.

Categories