How to split abbreviated days of the week? - php

I'm trying to split the string MTWTHFS (days of the week) and store it as an array.
I've tried str_split() but it will also split T and H for Thursday.
if(strpos($days, 'TH') !== false) {
$withth = str_replace("TH", "" ,$days);
}else{
$day = str_split($days);
print_r($day);
}
I also tried first removing the TH in the string before splitting it.
str_split() is fine when there is no TH in the string.
This is my desired result:
array([0]=>'M', [1]=>'T', [2]=>'W', [3]=>'TH', [4]=>'F', [5]=>'S')
Are there any better ways to do this?

I'm sure there will be a few ways to do this. Here is one way with preg_split()
Code: (PHP Demo)
$days = 'MTWTHFS';
$split = preg_split('/.H?\K/', $days, 0, PREG_SPLIT_NO_EMPTY);
var_export($split);
Output:
array (
0 => 'M',
1 => 'T',
2 => 'W',
3 => 'TH',
4 => 'F',
5 => 'S',
)
Pattern Demo
For anyone who wishes to use a non-regex method for any reason, this will do the same:
$days = 'MTWTHFS';
for ($i = 0, $k = -1, $len = strlen($days); $i < $len; ++$i) {
if ($days[$i] == 'H') {
$split[$k] .= 'H';
} else {
$split[++$k] = $days[$i];
}
}
It seems PHP 5.5 doesn't like \K in the pattern. This will provide the desired result:
$split = preg_split('/(.H?)/', $days, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

Related

How to increment a string with a custom character set

Given a custom alphabet like ['f', 'h', 'z', '#', 's']
I'd like to take a string written in this "alphabet" like
ffff#zz and increment it
So for example if the string was sss After incrementing it will look like hfff the same way that if you increment 999 you get 1000
My current attempt is here: https://3v4l.org/FjAsd
Given:
$characters = ['a', 'b', 'c'];
$string = 'cccc';
my code can do:
baaaa
but if give it
$characters = ['a', 'b', 'c'];
$string = 'aaa';
it will return
b
When I expected aab
The way this works is by processing the string from the end, each time you look at a character you check it's position in the array (I use a flipped array as it's more efficient than using array_search() each time). Then if the character is at the end of the array, then set it to the 0th element of the alphabet and increment the next digit to the left. If there is another letter from the alphabet to increment the current value, then just replace it and stop the loop.
The last bit is that if you have processed every character and the loop was still going, then there is a carry - so add the 0th digit to the start.
$characters = ['a', 'b', 'c'];
$string = 'cccc';
$index = array_flip($characters);
$alphabetCount = count($index)-1;
for ( $i = strlen($string)-1; $i >= 0; $i--) {
$current = $index[$string[$i]]+1;
// Carry
if ( $current > $alphabetCount ) {
$string[$i] = $characters[0];
}
else {
// update and exit
$string[$i] = $characters[$current];
break;
}
}
// As reached end of loop - carry
if ( $i == -1 ) {
$string = $characters[0].$string;
}
echo $string;
gives
aaaaa
with
$characters = ['f', 'h', 'z', '#', 's'];
$string = 'ffff#zz';
you get
ffff#z#
I ended up with something like this:
$string = 'ccc';
$alphabet = ['a', 'b', 'c'];
$numbers = array_keys($alphabet);
$numeric = str_replace($alphabet, $numbers, $string);
$base = count($alphabet) + 1;
$decimal = base_convert($numeric, $base, 10);
$string = base_convert(++$decimal, 10, $base);
strlen($decimal) !== strlen($string)
and $string = str_replace('0', '1', $string);
echo str_replace($numbers, $alphabet, $string);
This one has the advantage of supporting multi byte characters

PHP for and loop array

I just want to get a list of words from an array which has more and less than 3 vowels, but separately how can I do that with only for loop?
$name = array('jake', 'rita', 'ali', 'addert', 'siryteee', 'skeueei', 'wsewwauie', 'aaaaweefio');
$vowels = array('a', 'e', 'i', 'o', 'u');
$massiv = [ ];
$vowel = [ ];
for ($i = 0; $i < count($name); $i++) {
$massiv[ ] = $name[$i];
for ($j = 0; $j < count($vowels); $j++) {
$vowel[ ] = $vowels[$j];
}
}
Use Preg Grep:
$name = array('jake', 'rita', 'ali', 'addert', 'siryteee', 'skeueei', 'wsewwauie', 'aaaaweefio');
$vowels = array('a', 'e', 'i', 'o', 'u');
$pattern = '/('.implode('|',$vowels).'){3}/i';
print_r(preg_grep($pattern, $name));
Output
Array
(
[4] => siryteee
[5] => skeueei
[6] => wsewwauie
[7] => aaaaweefio
)
Sandbox
The regex is pretty strait forward
/(a|e|i|o|u){3}/i
Match anything with at least 3 of any combination of the following a|e|i|o|u. The (..) is a capture group, the | is or, the {3} is match 3 times. You could use {3,} 3 or more, but it doesn't really matter in this case because as soon as you have 3 you can quite looking. The \i flag makes it case insensitive.
As long as each array item is a single word this should work fine, if you have multiple words it will be much more difficult to come up with a match.
For Loop only
Here I will do you the favor
$name = array('jake', 'rita', 'ali', 'addert', 'siryteee', 'skeueei', 'wsewwauie', 'aaaaweefio');
$vowels = array('a', 'e', 'i', 'o', 'u');
$matches = [];
for ($i = 0; $i < count($name); $i++) {
$total = 0;
for ($j = 0; $j < count($vowels); $j++) {
$total += substr_count($name[$i], $vowels[$j]);
if($total > 2){
$matches[] = $name[$i];
break; //exit inner loop
}
}
}
print_r($matches);
Sandbox
The main thing here is
int substr_count ( string $haystack, string $needle [, int $offset = 0 [, int $length ]] )
substr_count() returns the number of times the needle substring occurs in the haystack string. Please note that needle is case sensitive.
http://php.net/manual/en/function.substr-count.php
Same output as the first one. However the nice thing about Preg Grep (besides case insensitivity) is it keeps the array keys, you can replicate this in the for each loop by adding the $i index in when adding matches:
$matches[$i] = $name[$i];
This keeps the matches correlated to the original array, something that may be useful in some cases.
If you want case insensitivity, the easy thing is to just lowercase the words. There are some edge cases where this won't work but for most English words it should be fine.
$name=strtolower($names[$i]);
//change $name to $names as it makes more sense
//when adding to the match array use $names[$i]
//that way we add the unmodified version to our matches
Also I should mention from a performance standpoint it's generally better to count outside of the for loop (condition).
$name_len = count($name);
for ($i = 0; $i < $name_len; $i++)
//another way to do it is like this
for($i=0,$n=count($name);$i<$n;$i++)
Summery
So putting all that togather
$names = array('jake', 'rita', 'ali', 'addert', 'siryteee', 'skeueei', 'wsewwauie', 'aaaaweefio');
$vowels = array('a', 'e', 'i', 'o', 'u');
$matches = [];
for ($i=0,$n=count($names);$i<$n;$i++) {
$total=0;
$name=strtolower($names[$i]);
for ($j=0,$v=count($vowels);$j<$v;$j++) {
//count lowercased version
$total += substr_count($name, $vowels[$j]);
if($total > 2){
$matches[$i] = $names[$i]; //use original in match
break; //exit inner loop
}
}
}
print_r($matches);
You get about 12 lines of code that does the equivalent of one preg_grep call.
LAST THING
The code you had was just transferring the words and vowels to another array. Except you would wind up with words*vowels more vowels. Because for each loop of the outer for loop (tied to words) you would do a full loop of all the vowels.
Anyway Enjoy!

Search associative array and print corresponding value in php

I have a dictionary like
UUU F
UUC F
CUU L
CUC L
UUA L
CUA L
UUG L
CUG L
AUU I
AUC I
AUA I
GUU V
GUC V
GUG V
GUA V
So Given a string I want to replace every 3 chars its respective char
I was thinking on using associative arrays:
$d = array();
$d['UUU']='F';
$d['UUC']='F';
$d['UUA']='L';
$d['CUU']='L';
$d['GUC']='V';
$d['GUG']='V';
$d['GUA']='V';
$d['UUG']='L';
$d['CUG']='L';
$s = "UUAGUAUUG";
$temp="";
for($i=0; $i<strlen($s)+1; $i++){
$temp .= $s[$i];
if($i%3==0){
echo $temp;
echo array_search($temp, $d);
$temp = "";
}
}
It should output LVL but have no success
Use str_split:
$s = 'UUAGUAUUG';
$split = str_split($s,3);
$translated = array();
foreach ($split as $bases) {
/**
* # supresses warnings if the array index doesn't exist
*/
$translated []= #$d[$bases];
}
echo join('',$translated);
I think this might work:
$temp = implode(array_map(function($a) { return $d[$a];}, str_split($s, 3)));
The basic solution is:
<?php
$dict = array(
'UUU' => 'F',
'UUC' => 'F',
'UUA' => 'L',
'CUU' => 'L',
'GUC' => 'V',
'GUG' => 'V',
'GUA' => 'V',
'UUG' => 'L',
'CUG' => 'L'
);
$before = "UUAGUAUUG";
$after = strtr($before, $dict);
Although you may be able to write a faster version that takes into account that you are replacing every three letters.
And I'm not 100% sure this will even work, given what kind of combinations can overlap over the 3-char barrier. Which leaves you with this rotten solution:
$after = str_replace('-', '',
strtr(preg_replace('/[A-Z]{3}/', '\0-', $before), $dict));
Seriously, don't try this at home. ;)
You're testing at the wrong time.
Change $i%3 == 0 to $i%3 == 2.
The reason here is that you have added to your temporary string first. That means you immediately check a string of length 1 ("U") and then clear it. Then you go around for another 3 times and you get "UAG", followed by "UAU". Neither of these are in your array.
What I don't understand is that you output the value of $temp each time this happens, so you should have picked up on this.
Change your for loop to this:
for($i=0; $i<strlen($s)+1; $i++){
$temp .= $s[$i];
if(($i+1)%3==0){
echo $d[$temp];
$temp = "";
}
}
Your i value starts from 0. And array_values does not give the expected answer.

"Unfolding" a String

I have a set of strings, each string has a variable number of segments separated by pipes (|), e.g.:
$string = 'abc|b|ac';
Each segment with more than one char should be expanded into all the possible one char combinations, for 3 segments the following "algorithm" works wonderfully:
$result = array();
$string = explode('|', 'abc|b|ac');
foreach (str_split($string[0]) as $i)
{
foreach (str_split($string[1]) as $j)
{
foreach (str_split($string[2]) as $k)
{
$result[] = implode('|', array($i, $j, $k)); // more...
}
}
}
print_r($result);
Output:
$result = array('a|b|a', 'a|b|c', 'b|b|a', 'b|b|c', 'c|b|a', 'c|b|c');
Obviously, for more than 3 segments the code starts to get extremely messy, since I need to add (and check) more and more inner loops. I tried coming up with a dynamic solution but I can't figure out how to generate the correct combination for all the segments (individually and as a whole). I also looked at some combinatorics source code but I'm unable to combine the different combinations of my segments.
I appreciate if anyone can point me in the right direction.
Recursion to the rescue (you might need to tweak a bit to cover edge cases, but it works):
function explodinator($str) {
$segments = explode('|', $str);
$pieces = array_map('str_split', $segments);
return e_helper($pieces);
}
function e_helper($pieces) {
if (count($pieces) == 1)
return $pieces[0];
$first = array_shift($pieces);
$subs = e_helper($pieces);
foreach($first as $char) {
foreach ($subs as $sub) {
$result[] = $char . '|' . $sub;
}
}
return $result;
}
print_r(explodinator('abc|b|ac'));
Outputs:
Array
(
[0] => a|b|a
[1] => a|b|c
[2] => b|b|a
[3] => b|b|c
[4] => c|b|a
[5] => c|b|c
)
As seen on ideone.
This looks like a job for recursive programming! :P
I first looked at this and thought it was going to be a on-liner (and probably is in perl).
There are other non-recursive ways (enumerate all combinations of indexes into segments then loop through, for example) but I think this is more interesting, and probably 'better'.
$str = explode('|', 'abc|b|ac');
$strlen = count( $str );
$results = array();
function splitAndForeach( $bchar , $oldindex, $tempthread) {
global $strlen, $str, $results;
$temp = $tempthread;
$newindex = $oldindex + 1;
if ( $bchar != '') { array_push($temp, $bchar ); }
if ( $newindex <= $strlen ){
print "starting foreach loop on string '".$str[$newindex-1]."' \n";
foreach(str_split( $str[$newindex - 1] ) as $c) {
print "Going into next depth ($newindex) of recursion on char $c \n";
splitAndForeach( $c , $newindex, $temp);
}
} else {
$found = implode('|', $temp);
print "Array length (max recursion depth) reached, result: $found \n";
array_push( $results, $found );
$temp = $tempthread;
$index = 0;
print "***************** Reset index to 0 *****************\n\n";
}
}
splitAndForeach('', 0, array() );
print "your results: \n";
print_r($results);
You could have two arrays: the alternatives and a current counter.
$alternatives = array(array('a', 'b', 'c'), array('b'), array('a', 'c'));
$counter = array(0, 0, 0);
Then, in a loop, you increment the "last digit" of the counter, and if that is equal to the number of alternatives for that position, you reset that "digit" to zero and increment the "digit" left to it. This works just like counting with decimal numbers.
The string for each step is built by concatenating the $alternatives[$i][$counter[$i]] for each digit.
You are finished when the "first digit" becomes as large as the number of alternatives for that digit.
Example: for the above variables, the counter would get the following values in the steps:
0,0,0
0,0,1
1,0,0 (overflow in the last two digit)
1,0,1
2,0,0 (overflow in the last two digits)
2,0,1
3,0,0 (finished, since the first "digit" has only 3 alternatives)

Most efficient way to delimit key

Say I have a string of 16 numeric characters (i.e. 0123456789012345) what is the most efficient way to delimit it into sets like : 0123-4567-8901-2345, in PHP?
Note: I am rewriting an existing system that is painfully slow.
Use str_split():
$string = '0123456789012345';
$sets = str_split($string, 4);
print_r($sets);
The output:
Array
(
[0] => 0123
[1] => 4567
[2] => 8901
[3] => 2345
)
Then of course to insert hyphens between the sets you just implode() them together:
echo implode('-', $sets); // echoes '0123-4567-8901-2345'
If you are looking for a more flexible approach (for e.g. phone numbers), try regular expressions:
preg_replace('/^(\d{4})(\d{4})(\d{4})(\d{4})$/', '\1-\2-\3-\4', '0123456789012345');
If you can't see, the first argument accepts four groups of four digits each. The second argument formats them, and the third argument is your input.
This is a bit more general:
<?php
// arr[string] = strChunk(string, length [, length [...]] );
function strChunk() {
$n = func_num_args();
$str = func_get_arg(0);
$ret = array();
if ($n >= 2) {
for($i=1, $offs=0; $i<$n; ++$i) {
$chars = abs( func_get_arg($i) );
$ret[] = substr($str, $offs, $chars);
$offs += $chars;
}
}
return $ret;
}
echo join('-', strChunk('0123456789012345', 4, 4, 4, 4) );
?>

Categories