PHP Using the array_filter - php

I will try to explain my situation as best as possible so bear with me.
I have an array with single words in them, for example:
This
is
a
test
array
Now i created another array that looks alike but with 2 words, which looks like this:
This is
is a
a test
test array
Ok here is where my problem starts. I have an array of 'common words' those words should be exluded from the array. Let's say those common words would be is and a for this example. Right now i search for common words first on the single word array so i can use if(in_array($word, $common_words)) continue; Which makes it skip the one if it's in the common_words array.
But this would result in this array:
This test
test array
But this is not how i want it to happen. It should be like this:
test array
Because this is the only 1 that had these 2 words next to eachother originally before we started to take out the 'common_words'. (are you still with me?)
The problem here is that if(in_array) doesn't work anymore if I have an array with 2 words. So i did some research and stumbled upon the array_filter command. I think this is what I need but i'm at a total loss as on how to use/apply it to my code.
I hope I explained it well enough for you to understand what my problem is and I would appreciate it if someone could help me.
Thanks in advance!

Your guess is correct, you can use:
$array = ['this is', 'array array', 'an array', 'test array'];
$stop = ['is', 'test'];
$array = array_filter($array, function($x) use ($stop)
{
return !preg_match('/('.join(')|(', $stop).')/', $x);
});
-i.e. exclude all items with certain words in it by pattern using array_filter()
This will work with filtering because it will match by regex, i.e. from $stop we'll get regex (is)|(test)
A good idea will be to evaluate regex separately so do not evaluate it each time inside array_filter() iteration, like:
$array = ['this is', 'array array', 'an array', 'test array'];
$stop = ['is', 'test'];
$pattern = '/('.join(')|(', $stop).')/';
$array = array_filter($array, function($x) use ($pattern)
{
return !preg_match($pattern, $x);
});
Important note#1: if your stop words may contain some special characters that will be treated in regex in special way, it's needed to use preg_quote() like:
$pattern = '/'.join('|', array_map(function($x)
{
return '('.preg_quote($x, '/').')';
}, $stop)).'/';
$array = array_filter($array, function($x) use ($pattern)
{
return !preg_match($pattern, $x);
});
Important note#2: If your array of stopwords is too long this may cause regex compilation fail because of it's length (too large). There are some tricks to overcome it, but if it's your case, you'd better to use strpos() instead:
$array = array_filter($array, function($x) use ($stop)
{
foreach($stop as $word)
{
if(false!==strpos($x, $word))
{
return false;
}
}
return true;
});

I think, the bes way, two operators: array_diff and array_unique
$a[] = 'This';
$a[] = 'is';
$a[] = 'is';
$a[] = 'a';
$a[] = 'a';
$a[] = 'test';
$a[] = 'test';
$a[] = 'array';
$excluded = array('is', 'a');
$result = array_diff($a, $excluded); // Remove all excluded words
$result = array_unique($result); // unique values
var_dump($result);
And result:
array (size=3)
0 => string 'This' (length=4)
5 => string 'test' (length=4)
7 => string 'array' (length=5)

Related

Explode long string - PHP

I have a long string like: B1C1F4G6H4I7J1J8L5O6P2Q1R6T5U8V1Z5, and i need to explode it on pairs so in this case 1 - B1 2 - C1 and so on...
The reason why i need that is that each pair contains 1 information in database so i have to take each of that pair and make some cycle or something to MySQL search
I was thinking about convert that sting to array like this:
1=> B1
2=> C1
3=> F4
and then foreach that with something like
foreach ($array as $arr)
{
//and here come database search
}
But maybe i am completly wrong and to say true everything i try finish with ERROR so guys i am up for any advise, if somebody have time to write an example code it will be awesome.
Thanks!
p.s.
First of all i was thinking about something like:
$string[0] = substr($string,0,2);
$string[1] = substr($string,2,4);
but string will changing and I never know how long it will be
You can use str_split()
$str = 'B1C1F4G6H4I7J1J8L5O6P2Q1R6T5U8V1Z5';
$ar = str_split($str,2);
print_r($ar);
Make sure you access the first element with zero index.
You can always access a string like an array in itself without having to do anything to it like this:
$string='B1C1F4G6H4I7J1J8L5O6P2Q1R6T5U8V1Z5';
for($i=0;$i<strlen($string)/2;$i++)
{
$arr[]=$string[$i*2].$string[($i*2)+1];
}
print_r($arr);
Php function str_split might work
>>> $text = 'B1C1F4G6H4I7J1J8L5O6P2Q1R6T5U8V1Z5'
>>> str_split($text, 2)
=> [
"B1",
"C1",
"F4",
"G6",
"H4",
"I7",
"J1",
"J8",
"L5",
"O6",
"P2",
"Q1",
"R6",
"T5",
"U8",
"V1",
"Z5"
]
You should use recursion:
$string = "B1C1F4G6H4I7J1J8L5O6P2Q1R6T5U8V1Z5";
$array = recursion($string);
print_r($array);
function recursion ($string, $array = array()){
if (strlen($string) < 1){
return $array;
}else{
$array[] = substr($string, 0 ,2);
return recursion(substr($string, 2), $array);
}
}

php - finding keys in an array that match a pattern

I have an array that looks like:
Array ( [2.5] => ABDE [4.8] => Some other value )
How would I find any key/value pair where the key matches a pattern? I will know the value of the first digit in the key,but not the second. so for example, using a prefix of "2.", I want to somehow be able to find the key "2.5" and return both the key and the value "ABDE".
I was thinking about using a regular expression with a pattern like:
$prefix = 2;
$pattern = '/'.$prefix.'\.\d/i';
and then looping through the array and checking each key. (by the way, just for demo purposes, $prefix has been hardcoded to 2, but in the real system, this is a value provided by the user's input).
I'm wondering if there's a simpler way to do this?
Thanks.
I think you need something like this:
$keys = array_keys($array);
$result = preg_grep($pattern, $keys);
The result will be a array that holds all the keys that match the regex. The keys can be used to retrieve the corresponding value.
Have a look at the preg_grep function.
you can simply loop through the array and check the keys
$array = array(...your values...);
foreach($array as $key => $value) {
if (preg_match($pattern,$key)){
// it matches
}
}
You can wrap it in a function and pass your pattern as parameter
Old question, but here's what I like to do:
$array = [ '2.5' => 'ABDE', '4.8' => 'Some other value' ];
preg_grep + array_keys will find all keys
$keys = preg_grep( '/^2\.\d/i', array_keys( $array ) );
You can add array_intersect_key and array_flip to extract a slice of the array that matches the pattern
$vals = array_intersect_key( $array, array_flip( preg_grep( '/^2\.\d/i', array_keys( $array ) ) ) );
For future programmers who encounter the same issue. Here is a more complete solution which doesn't use any loops.
$array = ['2.5'=> 'ABCDE', '2.9'=>'QWERTY'];
$keys = array_keys($array);
$matchingKeys = preg_grep('/^2\.+/', $keys);
$filteredArray = array_intersect_key($array, array_flip($matchingKeys));
print_r($filteredArray);
That are my ways
$data = ["path"=>"folder","filename"=>"folder/file.txt","required"=>false];
// FIRST WAY
$keys = array_keys($data);
if (!in_array("path", $keys) && !in_array("filename",$keys) && !in_array("required",$keys)) {
return myReturn(false, "Dados pendentes");
}
// SECOND WAY
$keys = implode("," array_keys($data));
if (!preg_match('/(path)|(filename)|(required)/'), $keys) {
return myReturn(false, "Dados pendentes");
}
To get just the part of the array with matching keys you could also write
$matching_array = array_flip(preg_grep($pattern, array_flip($your_array)));
This might just lead to problems performance-wise, if your array gets too big.
There's T-Regx library for regular expression in PHP, and it kas preg::grep_keys() method.
<?php
$array = [2.5 => 'ABDE', 4.8 => 'Some other value'];
preg::grep_keys("/$prefix\.\d/i", $array);

How to match rows in array to an array of masks?

I have array like this:
array('1224*', '543*', '321*' ...) which contains about 17,00 "masks" or prefixes.
I have a second array:
array('123456789', '123456788', '987654321' ....) which contain about 250,000 numbers.
Now, how can I efficiently match every number from the second array using the array of masks/prefixes?
[EDIT]
The first array contains only prefixes and every entry has only one * at the end.
Well, here's a solution:
Prelimary steps:
Sort array 1, cutting off the *'s.
Searching:
For each number in array 2 do
Find the first and last entry in array 1 of which the first character matches that of number (binary search).
Do the same for the second character, this time searching not the whole array but between first and last (binary search).
Repeat 2 for the nth character until a string is found.
This should be O(k*n*log(n)) where n is the average number length (in digits) and k the number of numbers.
Basically this is a 1 dimensional Radix tree, for optimal performance you should implement it, but it can be quite hard.
My two cents....
$s = array('1234*', '543*', '321*');
$f = array('123456789', '123456788', '987654321');
foreach ($f as $haystack) {
echo $haystack."<br>";
foreach ($s as $needle) {
$needle = str_replace("*","",$needle);
echo $haystack "- ".$needle.": ".startsWith($haystack, $needle)."<br>";
}
}
function startsWith($haystack, $needle) {
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
To improve performance it might be a good idea to sort both arrays first and to add an exit clause in the inner foreach loop.
By the way, the startWith-function is from this great solution in SO: startsWith() and endsWith() functions in PHP
Another option would to be use preg_grep in a loop:
$masks = array('1224*', '543*', '321*' ...);
$data = array('123456789', '123456788', '987654321' ....);
$matches = array();
foreach($masks as $mask) {
$mask = substr($mask, 0, strlen($masks) - 2); // strip off trailing *
$matches[$mask] = preg_grep("/^$mask/", $data);
}
No idea how efficient this would be, just offering it up as an alternative.
Although regex is not famous for being fast, I'd like to know how well preg_grep() can perform if the pattern is boiled down to its leanest form and only called once (not in a loop).
By removing longer masks which are covered by shorter masks, the pattern will be greatly reduced. How much will the reduction be? of course, I cannot say for sure, but with 17,000 masks, there are sure to be a fair amount of redundancy.
Code: (Demo)
$masks = ['1224*', '543*', '321*', '12245*', '5*', '122488*'];
sort($masks);
$needle = rtrim(array_shift($masks), '*');
$keep[] = $needle;
foreach ($masks as $mask) {
if (strpos($mask, $needle) !== 0) {
$needle = rtrim($mask, '*');
$keep[] = $needle;
}
}
// now $keep only contains: ['1224', '321', '5']
$numbers = ['122456789', '123456788', '321876543234567', '55555555555555555', '987654321'];
var_export(
preg_grep('~^(?:' . implode('|', $keep) . ')~', $numbers)
);
Output:
array (
0 => '122456789',
2 => '321876543234567',
3 => '55555555555555555',
)
Check out the PHP function array_intersect_key.

Ordered array to associative array, odd values as keys

Pretty straightforward:
// turn
array('foo', 'bar', 'hello', 'world');
// into
array('foo' => 'bar', 'hello' => 'world');
Right now I'm using:
do{
$arrayOut[current($arrayIn)] = next($arrayIn);
}while(next($arrayIn));
I'm wondering if there's a way to do it without the intermediary variable, $arrayOut. I could write a function, however this is a single use case, and I'm trying to keep my script uncluttered. I'm just wondering if there's something I missed in the docs that would serve this purpose.
The values are coming from a routing path:
route/to/controller/action/key1/value1/key2/value2
It's exploded, and eventually after using the other components, I'm left with ('key1', 'value1', 'key2', 'value2', ...)
Thank you folks for the insight and suggestions. Long Ears won this one for the concise approach, which when expanded to more than "1 line", is not terribly cryptic (I don't think at least)
However, also with regard to Long Ears' suggestion, perhaps my desire for semantically precise code with minimal verbosity has gotten the better of me, and I was chasing daisies trying to keep my variable scope "pollutant-free", to paraphrase myself.
You can make your existing code slightly more efficient (removes one function call per iteration, for one at the beginning:)
$b = array();
array_unshift($a, false);
while (false !== $key = next($a)) {
$b[$key] = next($a);
}
Ok, (shudder) here's your one liner:
$b = call_user_func_array('array_merge', array_map(function($v) { return array($v[0] => $v[1]); }, array_chunk($a, 2)));
You do not need an array, you can do it directly with the path text:
$path = "foo/bar/hello/world";
preg_match_all("#(?J)(?<key>[^/]+)/(?<val>[^/]*)#", $path, $p);
$params = array_combine($p['key'], $p['val']);
print_r($params);
/*
Array
(
[foo] => bar
[hello] => world
)
*/
I don't think you can do better than what you have. But what about trying to put the odd and even elements in different arrays when your input is read? You could then use array_combine to make an one-liner.
Update: array_map won't help, as it will produce an array with an equal number of elements. You can do things with array_filter and array_combine, but again, that won't end up being shorter.
I really like what you have; short, simple, does the job. I 'd simply refactor it out into a function, let's say array_interleave_combine or some such.
Update 2:
Well, you asked for it, so here it is. Tries to be too clever if you ask me. Is an one-liner, but I really really don't think the "pureness" of this is enough to pay back the lost time someone would need to understand what's going on:
$result = array_reduce(
array('foo', 'bar', 'hello', 'world'),
function($partial, $item) {
static $nextKey;
if ($nextKey === null) {
$nextKey = $item;
}
else {
$partial[$nextKey] = $item;
$nextKey = null;
}
return $partial;
});
This solution does not need an additional array:
$arr = array('foo', 'bar', 'hello', 'world');
for($i = 0, $count = count($arr); $i < $count; $i += 2)
{
$arr[$arr[$i]] = $arr[$i + 1];
unset($arr[$i], $arr[$i + 1]);
}

Search an array for a matching string

I am looking for away to check if a string exists as an array value in an array is that possible and how would I do it with PHP?
If you simply want to know if it exists, use in_array(), e.g.:
$exists = in_array("needle", $haystack);
If you want to know its corresponding key, use array_search(), e.g.:
$key = array_search("needle", $haystack);
// will return key for found value, or FALSE if not found
You can use PHP's in_array function to see if it exists, or array_search to see where it is.
Example:
$a = array('a'=>'dog', 'b'=>'fish');
in_array('dog', $a); //true
in_array('cat', $a); //false
array_search('dog', $a); //'a'
array_search('cat', $a); //false
Php inArray()
Incidentally, although you probably should use either in_array or array_search like these fine gentlemen suggest, just so you know how to do a manual search in case you ever need to do one, you can also do this:
<?php
// $arr is the array to be searched, $needle the string to find.
// $found is true if the string is found, false otherwise.
$found = false;
foreach($arr as $key => $value) {
if($value == $needle) {
$found = true;
break;
}
}
?>
I know it seems silly to do a manual search to find a string - and it is - but you may one day wish to do more complicated things with arrays, so it's good to know how to actually get at each $key-$value pair.
Here you go:
http://www.php.net/manual/en/function.array-search.php
The array_search function does exactly what you want.
$index = array_search("string to search for", $array);
Say we have this array:
<?php
$array = array(
1 => 'foo',
2 => 'bar',
3 => 'baz',
);
?>
If you want to check if the element 'foo' is in the array, you would do this
<?php
if(in_array('foo', $array)) {
// in array...
}else{
// not in array...
}
?>
If you want to get the array index of 'foo', you would do this:
<?php
$key = array_search('foo', $array);
?>
Also, a simple rule for the order of the arguments in these functions is: "needle, then haystack"; what you're looking for should be first, and what you're looking in second.

Categories