I need to replace multiple sections of a string based on their indices.
$string = '01234567890123456789';
$replacements = array(
array(3, 2, 'test'),
array(8, 2, 'haha')
);
$expected_result = '012test567haha0123456789';
Indices in $replacements are expected not to have overlaps.
I have been trying to write my own solution, split the original array into multiple pieces based on sections which needs to be replaced or not, and finally combine them:
echo str_replace_with_indices($string, $replacements);
// outputs the expected result '012test567haha0123456789'
function str_replace_with_indices ($string, $replacements) {
$string_chars = str_split($string);
$string_sections = array();
$replacing = false;
$section = 0;
foreach($string_chars as $char_idx => $char) {
if ($replacing != (($r_idx = replacing($replacements, $char_idx)) !== false)) {
$replacing = !$replacing;
$section++;
}
$string_sections[$section] = $string_sections[$section] ? $string_sections[$section] : array();
$string_sections[$section]['original'] .= $char;
if ($replacing) $string_sections[$section]['new'] = $replacements[$r_idx][2];
}
$string_result = '';
foreach($string_sections as $s) {
$string_result .= ($s['new']) ? $s['new'] : $s['original'];
}
return $string_result;
}
function replacing($replacements, $idx) {
foreach($replacements as $r_idx => $r) {
if ($idx >= $r[0] && $idx < $r[0]+$r[1]) {
return $r_idx;
}
}
return false;
}
Is there any more effective way to achieve the same result?
The above solution doesn't look elegant and feels quite long for string replacement.
Use this
$str = '01234567890123456789';
$rep = array(array(3,3,'test'), array(8,2,'haha'));
$index = 0;
$ctr = 0;
$index_strlen = 0;
foreach($rep as $s)
{
$index = $s[0]+$index_strlen;
$str = substr_replace($str, $s[2], $index, $s[1]);
$index_strlen += strlen($s[2]) - $s[1];
}
echo $str;
Related
I want to count the number of occurrences of each character in a string and print the ones that occur at least Y times.
Example :
Examples func(X: string, Y: int):
func("UserGems",2) => ["s" => 2, "e" => 2]
func("UserGems",3) => []
This what I could achieve so far:
$str = "PHP is pretty fun!!";
$strArray = count_chars($str, 1);
$num = 1;
foreach ($strArray as $key => $value) {
if ($value = $num) {
echo "The character <b>'".chr($key)."'</b> was found $value time(s)
<br>";
}
}
Firstly, you need to list all letters count with separately and to calculate it. Also, you need to calculate elements equal to count which is your find. I wrote 3 types it for your:
<?php
function check($string,$count) {
$achives = [];
$strings = [];
$strArray = count_chars($string, 1);
if($count) {
foreach($strArray as $char => $cnt) {
if($cnt==$count) {
$achives[chr($char)] = $cnt;
}
}
}
return $achives;
}
echo '<pre>';
print_r(check("aa we are all theere Tural a1",1));
So, it is very short version
function check($string,$count = 1) {
$achives = [];
$strArray = count_chars($string, 1);
array_walk($strArray,function($cnt,$letter) use (&$achives,$count){
$cnt!==$count?:$achives[chr($letter)] = $cnt;
});
return $achives;
}
echo '<pre>';
print_r(check("aa we are all theere Tural a1",3));
But it is exactly answer for your question:
<?php
function check($string,$count) {
$achives = [];
$strings = [];
$strArray = str_split($string, 1);
foreach($strArray as $index => $char ){
$strings[$char] = isset($strings[$char])?++$strings[$char]:1;
}
if($count) {
foreach($strings as $char => $cnt) {
if($cnt==$count) {
$achives[$char] = $cnt;
}
}
}
return $achives;
}
<?php
function check($string,$count) {
$achives = [];
$strings = [];
$strArray = count_chars($string, 1);
if($count) {
foreach($strArray as $char => $cnt) {
if($cnt==$count) {
$achives[chr($char)] = $cnt;
}
}
}
return $achives;
}
echo '<pre>';
print_r(check("aa we are all theere Tural a1",1));
You can simply do this with php str_split() and array_count_values() built in functions. i.e.
$chars = str_split("Hello, World!");
$letterCountArray = array_count_values($chars);
foreach ($letterCountArray as $key => $value) {
echo "The character <b>'".$key."'</b> was found $value time(s)\n";
}
Output
I have a comma separated string like
$str = "word1,word2,word3";
And i want to make a parent child relationship array from it.
Here is an example:
Try this simply making own function as
$str = "word1,word2,word3";
$res = [];
function makeNested($arr) {
if(count($arr)<2)
return $arr;
$key = array_shift($arr);
return array($key => makeNested($arr));
}
print_r(makeNested(explode(',', $str)));
Demo
function tooLazyToCode($string)
{
$structure = null;
foreach (array_reverse(explode(',', $string)) as $part) {
$structure = ($structure == null) ? $part : array($part => $structure);
}
return $structure;
}
Please check below code it will take half of the time of the above answers:
<?php
$str = "sports,cricket,football,hockey,tennis";
$arr = explode(',', $str);
$result = array();
$arr_len = count($arr) - 1;
$prev = $arr_len;
for($i = $arr_len; $i>=0;$i--){
if($prev != $i){
$result = array($arr[$i] => $result);
} else {
$result = array ($arr[$i]);
}
$prev = $i;
}
echo '<pre>',print_r($result),'</pre>';
Here is another code for you, it will give you result as you have asked :
<?php
$str = "sports,cricket,football,hockey,tennis";
$arr = explode(',', $str);
$result = array();
$arr_len = count($arr) - 1;
$prev = $arr_len;
for($i = $arr_len; $i>=0;$i--){
if($prev != $i){
if($i == 0){
$result = array($arr[$i] => $result);
}else{
$result = array(array($arr[$i] => $result));
}
} else {
$result = array ($arr[$i]);
}
$prev = $i;
}
echo '<pre>',print_r($result),'</pre>';
I have array with show names like this:
$shows = array('morning_show_15_02_2014_part2.mp3',
'morning_show_15_02_2014_part1.mp3',
'morning_show_14_02_2014_part2.mp3',
'morning_show_14_02_2014_part1.mp3',
'morning_show_13_02_2014_part2.mp3',
'morning_show_13_02_2014_part1.mp3');
So the list look like:
morning_show_15_02_2014_part2.mp3
morning_show_15_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
This is what i get when i loop the directory.
But the list should look like this:
morning_show_15_02_2014_part1.mp3
morning_show_15_02_2014_part2.mp3
morning_show_14_02_2014_part1.mp3
morning_show_14_02_2014_part2.mp3
morning_show_13_02_2014_part1.mp3
morning_show_13_02_2014_part2.mp3
Still ordered by date, but part 1 is first and then comes part 2.
How can i get this list into right order?
Thank you for any help!
Resolved!
Code is prett nasty but i got what i was looking for:
public function getMp3ListAsJSONArray() {
$songs = array();
$mp3s = glob($this->files_path . '/*.mp3');
foreach ($mp3s as $key => $mp3Source) {
$mp3Source = basename($mp3Source);
$mp3Title = substr($mp3Source, 4);
$mp3Title = substr($mp3Title, 0, -4);
$mp3Title = basename($mp3Source, ".mp3");
$mp3Title = str_replace('_', ' ', $mp3Title);
$mp3Title = ucfirst($mp3Title);
$songs[$key]['title'] = $mp3Title;
$songs[$key]['mp3'] = urldecode($this->files_url . '/' . $mp3Source);
}
rsort($songs);
$pairCounter = 1;
$counter = 0;
foreach ($songs as $key => $value) {
$playlist[$pairCounter][] = $value;
$counter = $counter + 1;
if($counter == 2) {
$pairCounter = $pairCounter + 1;
$counter = 0;
}
}
foreach ($playlist as $show) {
$finalList[] = $show[1];
$finalList[] = $show[0];
}
$finalList = json_encode($finalList);
return $finalList;
}
Output is like i described in the topic.
Try to use array sort
Here is an example for you
http://techyline.com/php-sorting-array-with-unique-value/
You must definitely write your own string comparision function. Remember that you have 2 different comparisons. The first compares the first parts for the filenames as strings. The second part compares the numbers, where 20 comes after 2. This is a natural number sorting for the second part. The third part is after the last dot in the filename. This will be ignored.
<?php
function value_compare($a, $b) {
$result = 0;
$descending = TRUE;
$positionA = strpos($a, 'part');
$positionB = strpos($b, 'part');
if ($positionA === $positionB) {
$compareFirstPart = substr_compare($a, $b, 0, $positionA + 1);
if ($compareFirstPart === 0) {
$length = 0;
$offset = $positionA + strlen('part');
$positionDotA = strrpos($a, '.');
$positionDotB = strrpos($b, '.');
$part2A = '';
$part2B = '';
if ($positionDotA !== FALSE) {
$part2A = substr($a, $offset, $positionDotA);
} else {
$part2A = substr($a, $offset);
}
if ($positionDotB !== FALSE) {
$part2B = substr($b, $offset, $positionDotB);
} else {
$part2B = substr($b, $offset);
}
$result = strnatcmp($part2A, $part2B);
} else {
$result = $compareFirstPart;
if ($descending) {
$result = -$result;
}
}
}
return $result;
}
$shows = array('morning_show_15_02_2014_part2.mp3', 'morning_show_15_02_2014_part1.mp3', 'morning_show_14_02_2014_part2.mp3', 'morning_show_14_02_2014_part1.mp3', 'morning_show_13_02_2014_part2.mp3', 'morning_show_13_02_2014_part1.mp3');
usort($shows, 'value_compare');
var_dump($shows);
?>
I want to combine strings in PHP. My script creates every possible combination like below.
$part1 = array('','d','n','s','g');
$part2 = array('a','e','o','oo');
$part3 = array('m','n','s','d','l','t','g','j','p');
$part4 = array('g','p','l','');
$part5 = array('g','p','l');
$part6 = array('a','e','o');
$part7 = array('d','l','r','');
$names = array();
foreach ($part1 as $letter1) {
foreach ($part2 as $letter2) {
foreach ($part3 as $letter3) {
foreach ($part4 as $letter4) {
foreach ($part5 as $letter5) {
foreach ($part6 as $letter6) {
foreach ($part7 as $letter7) {
$names[] = $letter1 . $letter2 . $letter3 . $letter4 . $letter5 . $letter6 . $letter7;
}
}
}
}
}
}
}
But I am not happy with my solution. I is quick and dirty code. Is there a solution wich works with a flexible number of part arrays, so I can extend the script by e.g. $part8 easiely? (without changing the loop construction)
Recursive one:
function buildNames( $parts, $chars = ''){
// Nothing to do, shouldn't happen
if( !count( $parts)){
return array();
}
$names = array();
$part = array_shift( $parts);
// Max level, we can build final names from characters
if( !count( $parts)){
foreach( $part as $char){
$names[] = $chars . $char;
}
return $names;
}
// "We need to go deeper" and build one more level with remembered chars so far
foreach( $part as $char){
$names = array_merge( $names, buildNames( $parts, $chars . $char));
}
return $names;
}
$parts = array( $part1, $part2, $part3, $part4, $part5, $part6, $part7);
$names = buildNames( $parts);
From head, from scratch, comment if something, but idea should be good
You could reduce this problem to six cartesian products:
cartesianProduct($part1,
cartesianProduct($part2,
cartesianProduct($part3,
cartesianProduct($part4,
cartesianProduct($part5,
cartesianProduct($part6, $part7))))));
function cartesianProduct($p1, $p2) {
$ret = array();
foreach($p1 as $l1)
foreach($p2 as $l2)
$ret[] = $l1 . $l2;
return $ret;
}
I have list of brands and want to provide a search function with highlighting. For example, there are the following brands
Apple
Cewe Color
L'Oréal
Microsoft
McDonald's
Tom Tailor
The user then types lor in search form. I'm using the following snippet for searching
class search {
private function simplify($str) {
return str_replace(array('&',' ',',','.','?','|','\'','"'), '', iconv('UTF-8', 'ASCII//TRANSLIT', $str));
}
public function do_search($search) {
$search = self::simplify($search);
$found = array();
foreach (self::$_brands as $brand) {
if (mb_strstr(self::simplify($brand['name']), $search) !== false) $found[]= $brand;
}
return $found;
}
}
That gives me:
Cewe Color
L'Oréal
Tom Tailor
How would be a highlighting possible? Like:
Cewe Co<b>lor</b>
L'<b>Oré</b>al
Tom Tai<b>lor</b>
Btw: I know, that most things can be done with str_replace(), but that fit my needs not in all cases
$highlighted = str_replace($search, "<b>$search</b>", $brand);
would be the simplest method.
:)
Works with FedEx also ;)
$_brands = array
(
"Apple",
"Cewe Color",
"L'Oréal",
"Microsoft",
"McDonald's",
"Tom Tailor"
);
$q = 'lor';
$search = clean($q);
foreach($_brands as $key => $brand){
$brand = clean($brand);
$x = stripos($brand, $search);
if($x !== false){
$regexp = NULL;
$l = strlen($q);
for($i = 0; $i < $l; $i++){
$regexp .= mb_strtoupper($q[$i]).'.?';
}
$regexp = substr($regexp, 0, strlen($regexp) - 2);
$new = $_brands[$key];
$new = preg_replace('#('.$regexp.')#ui', '<b>$0</b>', $new);
echo $new."<br />";
}
}
function clean($string){
$string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
$string = preg_replace('#[^\w]#ui', '', $string);
return $string;
}
self::$_brands contains result from database (containing columns name, name_lower, name_translit, name_simplified)
class search {
private function translit($str) {
return iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', str_replace(array('ä', 'ü', 'ö', 'ß'), array('a', 'u', 'o', 's'), mb_strtolower($str)));
}
private function simplify($str) {
return preg_replace('/([^a-z0-9])/ui', '', self::translit($str));
}
public function do_search($simplified) {
$found = array();
foreach (self::$_brands as $brand) {
if (mb_strstr($brand['name_simplified'], $simplified) !== false) $found[]= $brand;
}
return $found;
}
private function actionDefault() {
$search = $_POST['search_fld'];
$simplified = self::simplify($search);
$result = self::do_search($simplified);
$brands = array();
foreach ($result as $brand) {
$hl_start = mb_strpos($brand['name_simplified'], $simplified);
$hl_len = mb_strlen($simplified);
$brand_len = mb_strlen($brand['name']);
$tmp = '';
$cnt_extra = 0;
$start_tag = false;
$end_tag = false;
for ($i = 0; $i < $brand_len; $i++) {
if (($i - $cnt_extra) < mb_strlen($brand['name_simplified']) && mb_substr($brand['name_translit'], $i, 1) != mb_substr($brand['name_simplified'], $i - $cnt_extra, 1)) $cnt_extra++;
if (($i - $cnt_extra) == $hl_start && !$start_tag) {
$tmp .= '<b>';
$start_tag = true;
}
$tmp .= mb_substr($brand['name'], $i, 1);
if (($i - $cnt_extra + 1) == ($hl_start + $hl_len) && !$end_tag) {
$tmp .= '</b>';
$end_tag = true;
}
}
if ($start_tag && !$end_tag) $tmp .= '</b>';
$brands[] = "" . $tmp . "";
}
echo implode(' | ', $brands);
}
}