Would you please give me some guidance on how to style one character in a string at specific index? the index of this string comes from an array and in some cases the array is empty, so I only need to style the character in the string if the array is not empty
$indices = array(74, 266);
$string = "CAGGACACTCTTTCTAGTGTTGATTCACCTCGAAGAAGGTCTGGCCTATTAAGAGATCAAGTTCAGTTGGTAAAAAGAAGCAACTCTGCTCGTTATGAGATAGTCCCGATTCAAGATCAACTATCATTTGAGAAGGGTTTCTTTATTGTAATCCGTGCATGCCAGTTGTTGGCTCAGAAGAATGAAGGCATTGTACTGGTGGGAGTCGCTGGTCCTTCAGGGGCCGGAAAGACCATGTTTACAGAAAAGATCCTGAATGTTATGCCTAGTATTGCAATCATAAACATGGACAACTACAATGATCCCAGTCGTATCATTGATGGAAACTTCGACG";
so how do I add a surround the character at the index 74 and 266 with a span so I can give it a different style?
my data is coming from the database so I need to make it dynamic.
Thanks
It's fairly easy: all you need is a few substrs in a loop and to keep track of the character count.
Here's a working code I made:
// zero-based indices
$indices = array(3, 10, 25);
// input
$in = 'abcDefghijKlmnopqrstuvwxyZ';
$openTag = '<b>';
$closeTag = '</b>';
$out = '';
$last = 0;
foreach($indices as $i) {
$fragment = substr($in, $last, $i-$last);
$letter = substr($in, $i, 1);
$last = $i+1;
$out .= $fragment . $openTag . $letter . $closeTag;
}
$out .= substr($in, $last);
// output
echo $out;
For this example, $out is abc<b>D</b>efghij<b>K</b>lmnopqrstuvwxy<b>Z</b>.
For convenience, here's it also as a function:
function highlightChars($text, $indices, $openTag, $closeTag) {
$out = '';
$last = 0;
foreach($indices as $i) {
$fragment = substr($text, $last, $i-$last);
$letter = substr($text, $i, 1);
$last = $i+1;
$out .= $fragment . $openTag . $letter . $closeTag;
}
$out .= substr($text, $last);
return $out;
}
Related
I use the parser for converting xls to csv http://code.google.com/p/php-excel-reader/
<?php
set_time_limit(300);
require_once 'excel_reader2.php';
$data = new Spreadsheet_Excel_Reader("file.xls", false, 'UTF-8');
$f = fopen('file.csv', 'w');
for($row = 1; $row <= $data->rowcount(); $row++)
{
$out = '';
for($col = 1; $col <= $data->colcount(); $col++)
{
$val = $data->val($row,$col);
// escape " and \ characters inside the cell
$escaped = preg_replace(array('#”#u', '#\\\\#u', '#[”"]#u'), array('"', '\\\\\\\\', '\"'), $val);
if(empty($val))
$out .= ',';
else
$out .= '"' . $escaped . '",';
}
// remove last comma (,)
fwrite($f, substr($out, 0, -1));
fwrite($f, "\n");
}
fclose($f);
?>
From some strange reason it skip cells with specials symbols - like ° or ®. How it can be fixed?
utf8_decode and html_entity_decode works for me:
<?php
set_time_limit(300);
require_once 'excel_reader2.php';
$data = new Spreadsheet_Excel_Reader("file.xls", false, 'UTF-8');
$f = fopen('file.csv', 'w');
for($row = 1; $row <= $data->rowcount(); $row++)
{
$out = '';
for($col = 1; $col <= $data->colcount(); $col++)
{
$val = $data->val($row,$col);
// escape " and \ characters inside the cell
$escaped = preg_replace(array('#”#u', '#\\\\#u', '#[”"]#u'), array('"', '\\\\\\\\', '\"'), $val);
$escaped = utf8_decode($escaped);
//$escaped = html_entity_decode($escaped);
if(empty($val))
$out .= ',';
else
$out .= '"' . $escaped . '",';
}
// remove last comma (,)
fwrite($f, substr($out, 0, -1));
fwrite($f, "\n");
}
fclose($f);
?>
Output:
"1","2","3","4","5"
"a","b","c","d","e"
"6","7","°","9","10"
"q","w","e","r","t"
"®","12","13","14","15"
"z","x","c","v","b"
Is there any way to add an increment value while imploding an array?
This is the piece of code I'd like to have the increment value:
$entries = '<ul class="repeatData"><li class="listEntry1">' . implode('</li><li class="listEntry'. $countme .'">', $data) . '</li></ul>';
I'd like somehow to make the variable $countme increment every time it implodes each array value, if this is even possible.
You cannot do this with implode, but look into applying an anonymous function to the array. You can probably do what you want with not much more code.
$entries = '<ul class="repeatData">';
$countme = 1;
array_walk($data, function ($element) use (&$entries, &$countme) {
$entries .= '<li class="listEntry'. $countme .'">'. $element . '</li>';
$countme++;
});
$entries .= "</ul>";
Explanation: I have written an anonymous function, told it about $entries and $counter (so it is a closure, in fact) so that it can modify them from inside its scope, and passed it to array_walk, which will apply it to all elements of the array.
There is no built in function for that. You have to write your own:
This function generalizes the problem and takes an array of glues and the data as arguments. You may refine it to fit more to your needs...
function custom_implode($glues, $pieces) {
$result = '';
while($piece = array_shift($pieces)) {
$result .= $piece;
$glue = array_shift($glues);
if(!empty($pieces)) {
$result .= $glue;
}
}
return $result;
}
Usage:
$glues = array();
for($i = 0; $i < $end; $i++) {
$glues []= '</li><li class="listEntry'. $i .'">';
}
echo custom_implode($glues, $data);
You can save the for loop which populates $glues if you customize the function a little bit more:
function custom_implode($start, $pieces) {
$result = '';
$counter = $start;
while($piece = array_shift($pieces)) {
$result .= $piece;
if(!empty($pieces)) {
$result .= '</li><li class="listEntry'. $counter .'">';
}
}
return $result;
}
To expand upon #ravloony's answer, you can use a mapping function with a counter to produce what you want, the following function could assist.
function implode_with_counter($glue, $array, $start, $pattern) {
$count = $start;
$str = "";
array_walk($array, function($value) use ($glue, $pattern, &$str, &$count) {
if (empty($str)) {
$str = $value;
} else {
$str = $str . preg_replace('/' . preg_quote($pattern, '/') . '/', $count, $glue) . $value;
$count++;
}
});
return $str;
}
Example use:
echo implode_with_counter(' ([count]) ', range(1,5), 1, '[count]');
// Output: 1 (1) 2 (2) 3 (3) 4 (4) 5
For your case:
$entries = '<ul class="repeatData"><li class="listEntry1">'
. implode_with_counter('</li><li class="listEntry[countme]">', $data, 2, '[countme]')
. '</li></ul>';
Update: Alternative
An alternative approach is to just implement a callback version of implode(), and provide a function. Which is a little more universally usable, than the pattern matching.
function implode_callback($callback, array $array) {
if (!is_callable($callback)) {
throw InvalidArgumentException("Argument 1 must be a callable function.");
}
$str = "";
$cIndex = 0;
foreach ($array as $cKey => $cValue) {
$str .= ($cIndex == 0 ? '' : $callback($cKey, $cValue, $cIndex)) . $cValue;
$cIndex++;
}
return $str;
}
Example use:
echo implode_callback(function($cKey, $cValue, $cIndex) {
return ' (' . $cIndex . ') ';
}, range(1,5));
// Output: 1 (1) 2 (2) 3 (3) 4 (4) 5
Your case:
$entries = '<ul class="repeatData"><li class="listEntry1">'
. implode_callback(function($cKey, $cValue, $cIndex) {
return '</li><li class="listEntry' . ($cIndex + 1) . '">';
}, $data)
. '</li></ul>';
No, implode doesn't work that way.
You will need to create your own function to do that.
You should also consider if this is what you really need. In both Javascript and CSS you can easily reference the n-th child of a node if you need to do that.
This is my function:
$words = explode('. ',$desc);
$out = '';
foreach ($words as $key => $value) {
$out .= $value;
$rand = rand(0, 4);
if ($rand == 2) {
$out .= "\n";
} else {
$out .= ' ';
}
}
In short it's inserting new lines after random dots, but in this case dots are removed.
How could I do explode('. ',$desc), and leave dots in where they are?
Just put them back in when you concatenate.
$words = explode('. ',$desc);
$out = '';
foreach ($words as $key => $value) {
$out .= $value.'.';
$rand = mt_rand(0, 4);
if ($rand == 2) {
$out .= "\n";
} else {
$out .= ' ';
}
}
You should also use mt_rand(), it is a much better version of rand() unless you like not really random results.
Positive look behind without regex subject.
$words = preg_split('/(?<=\.)(?!\s*$)/', $desc);
Split at any non-character with a dot behind it.
Try this code..
$words = explode('. ',$desc);
$out = '';
foreach ($words as $key => $value) {
$out .= $value;
$rand = rand(0, 4);
if ($rand == 2) {
$out .= "<br>";
} else {
$out .= ' ';
}
}
i have some big string, and some array of words that must be replaced with some changes, like wrapping in link. First issue is wrap whole words or combinations words. And the second issue is do previous step minimum every 1000 characters.
$string="lalala word lalala blah, blah lalala combination of words lalala lalala...";
$patterns=array('word','combination of words');
$replacements=array('word','combination of words');
For an example, what i must to do with snippet before?
It sounds to me like you're looking for wordwrap(). You can then use preg_replace_callback() to apply it to your search patterns and make the replacements:
foreach ($patterns as $pattern) {
$regex = '/' . preg_quote($pattern, '/') . '/';
$string = preg_replace_callback($regex, function($match) {
return '<a href="#">'
. wordwrap(htmlspecialchars($match), 1000, '<br />')
. '</a>';
}, $string);
}
SOLUTION:
<?php
function set_keys_by_words($content, $key, $words,$before,$after) {
$positions = array();
$string = '';
for ($i = 0; $i < count($words); $i++) {
$string = preg_replace('/\b' . $words[$i] . '\b/ui', $key . $words[$i], $content);
$position = mb_strpos($string, $key);
if ($position != '') {
$positions[(int) $position] = $words[$i];
}
}
ksort($positions);
$word = '';
$number = '';
$i = 0;
foreach ($positions as $k => $v) {
$i++;
if ($i == 1) {
$number = $k;
$word = $v;
}
}
if ((int) $number) {
$word_len = strlen($word);
$part_after = preg_replace('/\b' . $word . '\b/ui', $before . $word . $after, mb_substr($content, 0, $number + $word_len));
echo $part_after . mb_substr($content, $number + $word_len, 1000);
$content = mb_substr($content, $number + $word_len + 1000);
if ($content != '') {
set_keys_by_words($content, $key, $words);
}
} else if ($number == '' && $content != '') {
echo $content;
}
}
?>
I am trying to determine the end of a foreach loop that is seeded with a collection of DOMNodeList. Currently, I am using a for loop would like to avoid having a 'magic' number there. I do know there are only going to be 8 columns, but I would like the code me generic for other applications.
Is it possible to convert this to a Foreach loop? I have tried the end() and next() functions, but they are not returning any data and I suspect that they only work on arrays and not this DOMNodeList collection.
The code is building a CSV file without the trailing ','
Current output is:
"Value 1","Value 2","Value 3","Value 4","Value 5","Value 6","Value 7","Value 8"
Here is an example of code:
$cols = $row->getElementsByTagName("td");
$printData = true;
// Throw away the header row
if ($isFirst && $printData) {
$isFirst = false;
continue;
}
for ($i = 0; $i <= 8; $i++) {
$output = iconv("UTF-8", "ASCII//IGNORE", $cols->item($i)->nodeValue);
$output2 = trim($output);
if ($i == 8) {
// Last Column
echo "\"" . $output2 . "\"" . "\n";
} else {
echo "\"" . $output2 . "\"" . ",";
}
}
You can use:
$cols->length
To retrieve the number of items in a DOMNodeList.
See http://php.net/manual/en/class.domnodelist.php
Edit:
If you change you're code to this, you don't have to worry about the trailing comma, or the length:
$output = array();
foreach ($cols as $item) {
$output = iconv("UTF-8", "ASCII//IGNORE", $item->nodeValue);
$output2 = trim($output);
$output[] = '"' . $output2 . '"';
}
$outputstring = implode(',', $output);
$cols->length
Should give you the number of items in the list
for ($i = 0; $i < $cols->length; $i++) {
// ...
if ($i == $cols->length - 1) {
// last column