Find and extract words from strings - php

I need to find and extract words (array) in a string. I tried many techniques, and I found how to find first occurrences, but can't get it to work recursively.
$uda_state = [
"uda actif",
"uda stagiaire",
"stagiaire uda",
"uda",
"apprenti actra",
"actra apprenti",
"actra"
];
$arr = [
'<p>Nothing here</p>',
'<p>UDA Stagiaire et ACTRA</p>',
'<p>Stagiaire UDA</p>',
'<p>Should not pop</p>'
];
function contains($str, array $arr)
{
foreach($arr as $a) {
if ($pos = stripos($str,$a)) return true;
}
return false;
}
function extractWord($str, array $arr)
{
foreach($arr as $a) {
if ($pos = stripos($str, $a)) return $arr;
}
}
function find_uda_actra($str, array $arr = []) {
global $uda_state;
if (contains($str, $uda_state)) {
$word = extractWord($str, $uda_state);
$arr[] = $word;
$str = str_ireplace($word, '', $str);
if ($str != '') {
if (contains($str, $uda_state)) {
list($str, $arr) = find_uda_actra($str, $arr);
}
else {
return [$str, $arr];
}
} else {
return [$str, $arr];
}
}
return [$str, $arr];
}
foreach ($arr as $str) {
list($content, $uda) = find_uda_actra($str);
echo 'Content:'.PHP_EOL;
var_dump($content);
echo 'UDA:'.PHP_EOL;
var_dump($uda);
}
All I get now is emptied content and the whole $uda_state in each $uda. I need only the one that was found.
3v4l link for sandbox.

Related

Multidimensional Array search by string

I have a multidimensional array that's contains all user data , and I've build a function to get array value with the given key .
the problem is that the array is multidimensional array , and I don't know how many level .
this is the function
function getUserSessionData($key)
{
$arrKeys = explode('.', $key);
if(count($arrKeys) == 1){
if(isset($_SESSION['user_data'][$arrKeys[0]])){
return $_SESSION['user_data'][$arrKeys[0]];
}
}
else{
if(isset($_SESSION['user_data'][$arrKeys[0]][$arrKeys[1]])){
return $_SESSION['user_data'][$arrKeys[0]][$arrKeys[1]];
}
}
return 0;
}
and this is an example of the call.
getUserSessionData('profile.firstName');
The (.) indicates of level of the array .
the function is support only tow levels .. is there any way to enhance this function so it can support more than tow levels ??
Sure, use a looping structure:
function getUserSessionData($key) {
$parts = explode('.', $key);
$data = $_SESSION["user_data"];
while (count($parts) > 0) {
$part = array_shift($parts);
$data = $data[$part];
}
return $data;
}
Or independently of the session:
function resolveKey($array, $key) {
$parts = explode('.', $key);
while (count($parts) > 0) {
$part = array_shift($parts);
$array = $array[$part];
}
return $array;
}
echo resolveKey(array(
"foo" => array(
"bar" => array(
"baz" => "ipsum"
)
)
), "foo.bar.baz"); // "ipsum"
echo resolveKey($_SESSION["user_data"], 'profile.firstName');
Here's a PHP-Fiddle
function getUserSessionData($key){
$arrKeys = explode('.', $key);
$data = $_SESSION['user_data'];
foreach($arrKeys as $k){
if(isset($data[$k])) $data = $data[$k];
else return false;
}
return $data;
}
Example usage:
session_start();
$_SESSION['user_data'] = [];
$_SESSION['user_data']['user'] = [];
$_SESSION['user_data']['user']['name'] = [];
$_SESSION['user_data']['user']['name']['first'] = "robert";
echo getUserSessionData("user.name.first"); // echos "robert"
Thank you #Halcyon
you've been very helpful.
but I've modified your function to get it to work .
this is the new function
function getUserSessionData($key) {
$data = Yii::app()->session['account_data']['user_data'];
$parts = explode('.', $key);
while (count($parts) > 0) {
$part = $parts[0];
if(!isset($data[$part])){
return 0;
}
$data = $data[$part];
array_shift($parts);
}
return $data;
}

in_array function, why this doesn't work?

I'm trying to validate a string to an array of numbers. If the string only contains numbers then the function should validate, but in_array isn't working, any suggestions?
$list = array(0,1,2,3,4,5,6,7,8,9);
$word = 'word';
$split = str_split($word);
foreach ($split as $s) {
if (!in_array($s, $list)) {
print 'asdf';
}
}
here is the class:
class Validate_Rule_Whitelist {
public function validate($data, $whitelist) {
if (!Validate_Rule_Type_Character::getInstance()->validate($data)) {
return false;
}
$invalids = array();
$data_array = str_split($data);
foreach ($data_array as $k => $char) {
if (!in_array($char, $whitelist)) {
$invalids[] = 'Invalid character at position '.$k.'.';
}
}
if (!empty($invalids)) {
$message = implode(' ', $invalids);
return $message;
}
return true;
}
}
in_array comparison with loosely typed values is somewhat strange. What would work in your case is:
$list = array('0','1','2','3','4','5','6','7','8','9');
$word = 'word';
$split = str_split($word);
foreach ($split as $s) {
if (!in_array($s, $list, true)) {
print 'asdf';
}
}
This compares strings with strings and results in no surprises.
But, as noted in the comments already, this is quite wrong way to do things and it is much better to use filter_var() or regular expressions** to achieve what you're trying.
it's ugly as sin but it works, no elegant solution here, just a double loop, if you see any problems please let me know
$match = array();
foreach ($data_array as $k => $char) {
foreach ($whitelist as $w) {
if (!isset($match[$k])) {
if ($char === $w) {
$match[$k] = true;
}
}
}
if (!isset($match[$k]) || $match[$k] !== true) {
$invalids[$k] = 'Invalid character at position '.$k.'.';
}
}
Something along the lines of this should work:
<?php
$validate_me = '123xyz';
if(preg_match("/[^0-9]/", $validate_me, $matches))
print "non-digit detected";
?>
update: add the $type = gettype ... settype($char, $type) to allow === to function correctly when checking for integers
foreach ($data_array as $k => $char) {
foreach ($whitelist as $w) {
if (!isset($match[$k])) {
$type = gettype($w);
if (gettype($char) !== $type) {
settype($char, $type);
}
if ($char === $w) {
$match[$k] = true;
}
}
}
...

Comparing in_array values

I have a array of val which has dynamic strings with underscores. Plus I have a variable $key which contains an integer. I need to match $key with each $val (values before underscore).
I did the following way:
<?php
$key = 2; //always a dynamic number
$val = array('3_33', '2_55'); //always a dynamic string with underscore
if(in_array($key, $val)) {
echo 'Yes';
}
else
{
echo 'No';
}
?>
Though this code works fine, I want to know if its a correct way or suggest some better alternative.
use this function for regex match from php.net
function in_array_match($regex, $array) {
if (!is_array($array))
trigger_error('Argument 2 must be array');
foreach ($array as $v) {
$match = preg_match($regex, $v);
if ($match === 1) {
return true;
}
}
return false;
}
and then change your code to use this function like this:
$key = 2; //always a dynamic number
$val = array('3_33', '2_55'); //always a dynamic string with underscore
if(in_array_match($key."_*", $val)) {
echo 'Yes';
}
else
{
echo 'No';
}
This should work :
foreach( $val as $v )
{
if( strpos( $v , $key .'_' ) === true )
{
echo 'yes';
}
else {
echo 'no';
}
}
you can use this
function arraySearch($find_me,$array){
$array2 =array();
foreach ($array as $value) {
$val = explode('_',$value);
$array2[] =$val[0];
}
$Key = array_search($find_me, $array2);
$Zero = in_array($find_me, $array2);
if($Key == NULL && !$Zero){
return false;
}
return $Key;
}
$key = 2; //always a dynamic number
$val = array('3_33', '2_55'); //always a dynamic string with underscore
$inarray = false;
foreach($val as $v){
$arr = explode("_", $val);
$inarray = $inarray || $arr[0] == $key
}
echo $inarray?"Yes":"No";
The given format is quite unpractically.
$array2 = array_reduce ($array, function (array $result, $item) {
list($key, $value) = explode('_', $item);
$result[$key] = $value;
return $result;
}, array());
Now you can the existence of your key just with isset($array2[$myKey]);. I assume you will find this format later in your execution useful too.

PHP transform array 'a', 'b', 'c' to 'a/b/c', 'a/b', 'a'

I have an uri and want to get an array of parts starting with full uri descent until I have only the first uri segment as part. I also have domain but the segments get stitched starting from the end back.
String 'a/b/c' to array('a/b/c', 'a/b', 'a')
String 'test.domain.com' to array('test.domain.com', 'domain.com', 'com')
Based on my initial method and the second method from the answers and a third method I like to know which one is the quickest and maybe there is a fourth method even better...
Method 1:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$segments = explode($delimiter, $string);
if ( ! $reverse)
{
array_walk($segments, function($v, $k, $d) use(&$segments, &$delimiter) {
$d[$k] = implode($delimiter, array_slice($segments, 0, (count($segments) - $k)));
}, &$parts);
}
else
{
array_walk($segments, function($v, $k, $d) use(&$segments, &$delimiter) {
$d[$k] = implode($delimiter, array_slice($segments, $k));
}, &$parts);
}
return $parts;
}
Method 2:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$segments = explode($delimiter, $string);
$parts = array();
while ($segments)
{
$parts[] = implode($delimiter, $segments);
if ($reverse)
{
array_shift($segments);
}
else
{
array_pop($segments);
}
}
return $parts;
}
Method 3:
function explode_special($delimiter, $string, $reverse = false)
{
if (strpos($string, $delimiter) === false) return array($string);
$parts = array($string);
for ($i = substr_count($string, $delimiter); $i > 0; $i--)
{
$parts[] = $string = $reverse
// ? trim(strstr($string, $delimiter), $delimiter)
? substr($string, strpos($string, $delimiter) + 1)
: substr($string, 0, strrpos($string, $delimiter));
}
return $parts;
}
Output:
// explode_special('/', 'a/b/c') = array('a/b/c', 'a/b', 'c');
// explode_special('.', 'test.domain.com', true) =
// array('test.domain.com', 'domain.com', 'com');
function multisplit($delim,$str) {
$parts = explode($delim,$str);
$arr = Array();
while($parts) {
$arr[] = implode($delim,$parts);
array_pop($parts);
}
return $arr;
}
With that function, you can just call multisplit("/","a/b/c"); and it will return ['a/b/c','a/b','a']. Similarly, call multisplit(".","test.domain.com"); and get ['test.domain.com','test.domain','test'].
I came up with the following
<?php
header("Content-type: text/plain"); //For display purposes only
/**
* Returns an array of strings with diminishing elements of $string according to $delimiter
*
* #param string $string The string
* #param string $delimiter The delimiter
*
* #return string[] Array of strings
*/
function degrading_split($string, $delimiter) {
$exploded = explode($delimiter, $string); //Array version of the string
$result = array(); //Initialize result array
foreach ($exploded as $key => $value) { //Iterate the array
$result[] = implode( #Implode...
$delimiter, #Using the original delimiter
array_slice($exploded, $key) #The complete $exploded array starting from the current $key
);
}
return $result;
}
print_r(degrading_split("a/b/c/d", "/"));
What you think about this with regex
function splitString($string,$delim) {
$arr = array();
preg_match_all('~['.$delim.']~U',$string,$delimCounter);
$arr[] = $string;
for ($i = 0;$i<count($delimCounter[0]);$i++) {
$string = $arr[] = preg_replace('~^[^.]*['.$delim.']~',"",$string);
}
return $arr;
}

Define multiple needles using stripos

How can i define multiple needles and still perform the same actions below. Im trying to define extra keywords such as numbers, numerals, etc... as of now i have to create a duplicate if loop with the minor keyword change.
if (stripos($data, 'digits') !== false) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
}
Create a function that loops through an array?
function check_matches ($data, $array_of_needles)
{
foreach ($array_of_needles as $needle)
{
if (stripos($data, $needle)!==FALSE)
{
return true;
}
}
return false;
}
if (check_matches($data, $array_of_needles))
{
//do the rest of your stuff
}
--edit added semicolon
function strposa($haystack, $needles=array(), $offset=0) {
$chr = array();
foreach($needles as $needle) {
$res = strpos($haystack, $needle, $offset);
if ($res !== false) $chr[$needle] = $res;
}
if(empty($chr)) return false;
return min($chr);
}
Usage:
$array = array('1','2','3','etc');
if (strposa($data, $array)) {
$arr = explode('+', $data);
for ($i = 1; $i < count($arr); $i += 2) {
$arr[$i] = preg_replace('/\d/', '', $arr[$i]);
}
$data = implode('+', $arr);
} else {
echo 'false';
}
function taken from https://stackoverflow.com/a/9220624/1018682
Though the previous answers are correct, but I'd like to add all the possible combinations, like you can pass the needle as array or string or integer.
To do that, you can use the following snippet.
function strposAll($haystack, $needles){
if(!is_array($needle)) $needles = array($needles); // if the $needle is not an array, then put it in an array
foreach($needles as $needle)
if( strpos($haystack, $needle) !== False ) return true;
return false;
}
You can now use the second parameter as array or string or integer, whatever you want.

Categories