How can I get part of the string with conditional prefix [+ and suffix +], and then return all of it in an array?
example:
$string = 'Lorem [+text+] Color Amet, [+me+] The magic who [+do+] this template';
// function to get require
function getStack ($string, $prefix='[+', $suffix='+]') {
// how to get get result like this?
$result = array('text', 'me', 'do'); // get all the string inside [+ +]
return $result;
}
many thanks...
You can use preg_match_all as:
function getStack ($string, $prefix='[+', $suffix='+]') {
$prefix = preg_quote($prefix);
$suffix = preg_quote($suffix);
if(preg_match_all("!$prefix(.*?)$suffix!",$string,$matches)) {
return $matches[1];
}
return array();
}
Code In Action
Here’s a solution with strtok:
function getStack ($string, $prefix='[+', $suffix='+]') {
$matches = array();
strtok($string, $prefix);
while (($token = strtok($suffix)) !== false) {
$matches[] = $token;
strtok($prefix);
}
return $matches;
}
Related
I need to identify unique urls from an array.
All of the following variants should count as equal:
http://google.com
https://google.com
http://www.google.com
https://www.google.com
www.google.com
google.com
I have the following solution:
public static function array_unique_url(array $array) : array
{
$uniqueArray = [];
foreach($array as $item) {
if(!self::in_array_url($item, $uniqueArray)){
$uniqueArray[] = $item;
}
}
return $uniqueArray;
}
public static function in_array_url(string $needle, array $haystack): bool {
$haystack = array_map([self::class, 'normalizeUrl'], $haystack);
$needle = self::normalizeUrl($needle);
return in_array($needle, $haystack);
}
public static function normalizeUrl(string $url) {
$url = strtolower($url);
return preg_replace('#^(https?://)?(www.)?#', '', $url);
}
However, this is not very efficient O(n^2). Can anybody point me to a better solution?
in_array is expensive. Instead of doing that create a hash and store values as their counts.
Something like:
$myHash = []; //a global array to hold values.
And while checking, Do this:
if(!empty($myHash[$needle] )){
//already exits
}
I haven't test it, but maybe something like this will work:
function getUniqueUrls(array $urls)
{
$unique_urls = [];
foreach ($urls as $url) {
$normalized_url = preg_replace('#^(https?://)?(www.)?#', '', strtolower($url));
$unique_urls[$normalized_url] = true;
}
return array_keys($unique_urls);
}
$arr = [
'http://google.com',
'https://google.com',
'http://www.google.com',
'https://www.google.com',
'www.google.com',
'google.com'
];
$unique_urls = getUniqueUrls($arr);
Here is a simplified version. It does not use preg_replace as it costs a lot. Also it does not make any unnecessary string operation.
$urls = array(
"http://google.com",
"https://google.com",
"http://www.google.com",
"https://www.google.com",
"www.google.com",
"google.com"
);
$uniqueUrls = array();
foreach($urls as $url) {
$subPos = 0;
if(($pos = stripos($url, "://")) !== false) {
$subPos = $pos + 3;
}
if(($pos = stripos($url, "www.", $subPos)) !== false) {
$subPos = $pos + 4;
}
$subStr = strtolower(substr($url, $subPos));
if(!in_array($subStr, $uniqueUrls)) {
$uniqueUrls[] = $subStr;
}
}
var_dump($uniqueUrls);
Another performance optimization could be implementing binary search on the unique urls because 'in_array' search the whole array as it is not sorted.
<?php
$urls = [
'http://google.com',
'https://google.com',
'http://www.google.com',
'https://www.google.com',
'www.google.com',
'google.com',
'testing.com:9200'
];
$uniqueUrls = [];
foreach ($urls as $url) {
$urlData = parse_url($url);
$urlHostName = array_key_exists('host',$urlData) ? $urlData['host'] : $urlData['path'];
$host = str_replace('www.', '', $urlHostName);
if(!in_array($host, $uniqueUrls) && $host != ''){
array_push($uniqueUrls, $host);
}
}
print_r($uniqueUrls);
?>
why you normlize your result array everytime?
here is a better solution with your code:
public static function array_unique_url(array $array): array
{
$uniqueArray = [];
foreach ($array as $item) {
if (!isset($uniqueArray[$item])) {
$uniqueArray[$item] = self::normalizeUrl($item);
}
}
return $uniqueArray;
}
public static function normalizeUrl(string $url)
{
return preg_replace('#^(https?://)?(www.)?#', '', strtolower($url));
}
When you want your original items you can use array_keys(array_unique_url($array))
for your normalized urls you don't need array_keys
Try this simplest solution. Here we are using two functions preg_replace and parse_url for achieving desired output
Try this code snippet here
<?php
$urls = array(
"http://google.com",
"https://google.com",
"http://www.google.com",
"https://www.google.com",
"www.google.com",
"google.com"
);
$uniqueUrls=array();
foreach($urls as $url)
{
$changedUrl= preg_replace("/^(https?:\/\/)?/", "http://", $url);//adding http to urls which does not contains.
$domain= preg_replace("/^(www\.)?/","",parse_url($changedUrl,PHP_URL_HOST));//getting the desired host and then removing its www.
preg_match("/^[a-zA-Z0-9]+/", $domain,$matches);//filtering on the basis of domains
$uniqueUrls[$matches[0]]=$domain;
}
print_r(array_values($uniqueUrls));
I'm looking for a way to replace all but first occurrences of a group or some character.
For example a following random string:
+Z1A124B555ND124AB+A555
1,5,2,4,A,B and + are repeating through out the string.
124, 555 are groups of characters that are also reoccurring.
Now, let's say I want to remove every but first occurrence of 555, A and B.
What regex would be appropriate? I could think of an example replacing all:
preg_replace('/555|A|B/','',$string);
Something like ^ that, but I want to keep the first occurrence... Any ideas?
Are your strings always delimited by plus signs? Do 555, A, and B always occur in the first "group" (delimited by +)?
If so, you can split, replace and then join:
$input = '+Z1A124B555+A124AB+A555';
$array = explode('+', $input, 3); // max 3 elements
$array[2] = str_replace(array('555', 'A', 'B'), '', $array[2]);
$output = implode('+', $array);
ps. No need to use regexes, when we can use a simple str_replace
Use the preg_replace_callback function:
$replaced = array('555' => 0, 'A' => 0, 'B' => 0);
$input = '+Z1A124B555+A124AB+A555';
$output = preg_replace_callback('/555|[AB]/', function($matches) {
static $replaced = 0;
if($replaced++ == 0) return $matches[0];
return '';
}, $input);
This solution could be modified to do what you want: PHP: preg_replace (x) occurrence?
Here is a modified solution for you:
<?php
class Parser {
private $i;
public function parse($source) {
$this->i=array();
return preg_replace_callback('/555|A|B/', array($this, 'on_match'), $source);
}
private function on_match($m) {
$first=$m[0];
if(!isset($this->i[$first]))
{
echo "I'm HERE";
$this->i[$first]=1;
}
else
{
$this->i[$first]++;
}
// Return what you want the replacement to be.
if($this->i[$first]>1)
{
$result="";
}
else
{
$result=$m[0];
}
return $result;
}
}
$sample = '+Z1A124B555ND124AB+A555';
$parse = new Parser();
$result = $parse->parse($sample);
echo "Result is: [$result]\n";
?>
A more generic function that works with every pattern.
function replaceAllButFirst($pattern, $replacement, $subject) {
return preg_replace_callback($pattern,
function($matches) use ($replacement, $subject) {
static $s;
$s++;
return ($s <= 1) ? $matches[0] : $replacement;
},
$subject
);
}
Having a string like
12345.find_user.find_last_name
how can i split at the charachter "." and convert it to a function-call:
find_last_name(find_user(12345));
and so on....could be of N-Elements (n-functions to run)....how do i do this effectivly, performance-wise also?
Edit, here is the solution based on your replies
thanks Gaurav for your great help. Here is my complete solution based on yours:
i protected the foreach with if(function_exists($function)){ to protect the whole thing from fatal php errors, and i added a complete example:
$mystring = '12345.find_user.find_last_name';
convert_string_to_functions($mystring);
function convert_string_to_functions($mystring){
$functions = explode('.', $mystring);
$arg = array_shift($functions);
foreach($functions as $function){
if(function_exists($function)){
$arg = $function($arg);
} else {
echo 'Function '.$function.' Not found';
}
}
echo $arg;
}
function find_last_name($mystring=''){
return $mystring.' i am function find_last_name';
}
function find_user($mystring=''){
return $mystring.' i am function find_user';
}
$string = '12345.find_user.find_last_name';
$functions = explode('.', $string);
$arg = array_shift($functions);
foreach($functions as $function){
$arg = $function($arg);
}
echo $arg;
preg_replace('/([a-z]+)([0-9]+)/', '$2$1', $str);
I want to store $1 and $2 in a variable. How can I store it in a variable?
Finding matches is the job of preg_match().
preg_match('/([a-z]+)([0-9]+)/', $str, $matches);
matches:
If matches is provided, then it is filled with the results of search. $matches[0] will contain the text that matched the full
pattern, $matches[1] will have the text that matched the first
captured parenthesized subpattern, and so on.
$full_pattern = $matches[0]
$a = $matches[1] // $1
$b = $matches[2] // $2
$c = $matches[3] // $3
$n = $matches[n] // $n
Use preg_replace_callback:
$one; $two;
function callback($matches)
{
global $one,$two;
$one = $matches[0];
$two = $matches[1];
return $matches[1].$matches[0];
}
preg_replace_callback('/([a-z]+)([0-9]+)/', 'callback', $str);
NOTE
Global is... not a good idea. I actually left that in because it is a concise, clear example of how to accomplish what you're trying to do. I would that I had never used the word, but global is there now and I don't feel right removing it. Actually, its existence saddens me deeply. You are far better off with something like this:
class Replacer
{
private $matches, $pattern, $callback;
public function __construct($pattern, $callback)
{
$this->pattern = $pattern;
$this->callback = $callback;
}
public function getPregCallback()
{
return array( $this, '_callback' );
}
public function _callback( array $matches )
{
$template = $this->pattern;
foreach( $matches as $key => $val )
{
if( $this->callback )
{
$matches[ $key ] = $val = $this->callback( $val );
}
$template = str_replace( '$' . ( $key + 1 ), $val, $template );
}
$this->matches = $matches;
return $template;
}
public function getMatches(){ return $this->matches; }
}
USE
// does what the first example did, plus it calls strtolower on all
// of the elements.
$r = new Replacer( '$2$1', 'strtolower' );
preg_replace_callback('/([a-z]+)([0-9]+)/', $r->getPregCallback(), $str);
list( $a, $b ) = $r->getMatches();
Use preg_replace_callback:
preg_replace_callback('/([a-z]+)([0-9]+)/', function($m) {
// save data
return $m[1].$m[0];
}, $str);
(This uses PHP 5.3 anonymous function syntax, on older PHP just define a normal function and pass its name as the second parameter.)
use preg_replace with "e" ( eval flag)
or better use
preg_replace_callback
http://php.net/manual/en/function.preg-replace-callback.php
see code here :
http://codepad.org/22Qh3wRA
<?php
$str = "a9";
preg_replace_callback('/([a-z]+)([0-9]+)/', "callBackFunc", $str);
function callBackFunc($matches)
{
var_dump($matches);
// here you can assign $matches elements to variables.
}
?>
If I had an array such as:
testarray = array('foo'=>34, 'bar'=>array(1, 2, 3));
How would I go about converting a string such as testarray[bar][0] to find the value its describing?
Well, you can do something like this (Not the prettiest, but far safer than eval)...:
$string = "testarray[bar][0]";
$variableBlock = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
$regex = '/^('.$variableBlock.')((\[\w+\])*)$/';
if (preg_match($regex, $string, $match)) {
$variableName = $match[1]; // "testarray"
if (!isset($$variableName)) {
//Error, the variable does not exist
return null;
} else {
$array = $$variableName;
if (preg_match_all('/\[(\w+)\]/', $match[2], $matches)) {
foreach ($matches[1] as $match) {
if (!is_array($array)) {
$array = null;
break;
}
$array = isset($array[$match]) ? $array[$match] : null;
}
}
return $array;
}
} else {
//error, not in correct format
}
You could use PHP's eval function.
http://php.net/manual/en/function.eval.php
However, make absolutely sure the input is sanitized!
Cheers