Make this block of php code dynamic or recursive - php

I've written the following block of code to find if a word exists in a grid of nodes.
function findWord() {
$notInLoc = [];
if ($list = $this->findAll("a")) {
foreach($list as $node) {
$notInLoc[] = $node->loc;
if ($list2 = $this->findAllConnectedTo($node, "r", $notInLoc)) {
foreach($list2 as $node2) {
$notInLoc[] = $node2->loc;
if ($list3 = $this->findAllConnectedTo($node2, "t", $notInLoc)) {
foreach($list3 as $node3) {
return true;
}
}
}
}
}
}
return false;
}
This "works" and passes all my 3-letter word test cases because I've hard-coded the characters I'm looking for and I know how long the word is. But what I need to do is pass in any word, regardless of length and letters, and return true if I found the word against all these restrictions.
To summarize the algorithm here:
1) I find all the nodes that contain the first character "a" and get a list of those nodes. That's my starting point.
2) For each "a" I'm looking for all the "r"s that are connected to it but not in a location I'm already using. (Each node has a location key, and that key is stored in the notInLoc array while looking through it. I realize that this may break though because notInLoc is only being reset the first time I enter the function so every time I go through the foreach it keeps pushing the same location in.
3) Once I've found all the "r"s connected to the "a" I'm currently on, I check to see if there are any "t"s connected to the "r"s. If there is at least 1 "t" connected, then I know the word has been found.
I'm having trouble refactoring this to make it dynamic. I'll give you the idea I was working with, but it is broken.
function inner($word, $list, $i = 0, $notInLoc = []) {
$i++;
foreach($list as $node) {
$notInLoc[] = $node->loc;
if ($list2 = $this->findAllConnectedTo($node, $word[$i], $notInLoc)) {
if ($i == (strlen($word) - 1)) {
return true;
} else {
$this->inner($word, $list2, $i, $notInLoc);
}
}
}
return false;
}
function findWord2($word) {
if ($list = $this->findAll($word[0])) {
return $this->inner($word, $list);
}
return false;
}
I understand that there are other ways to solve problems like this, but I need it to work using only the functions findAll which returns all nodes with a specific value, or false and findAllConnectedTo which returns all nodes with a specific value connected to a node that are not contained on the "Do Not Use" notInLoc list.

You need to pass result through all nested contexts to the top, because found word will eventually return true, but it will vanish in upper level (continue loop and return false). Try this:
if ($list2 = $this->findAllConnectedTo($node, $word[$i], $notInLoc)) {
if ($i == strlen($word) - 1 || $this->inner($word, $list2, $i, $notInLoc)) {
return true;
}
}
Next I'd take care of $word needlesly passed around. It stays the same for all contexts - only pointer changes.

Related

Removing an array from a PHP JSON object

So a bit of background information is I'm creating a web app and I have 50~ arrays that I'm currently using what I get from an API, I've created a script to find the arrays that I don't need lets call them "bad arrays" but the problem is I'm unsure how I can filter these arrays out with the method I'm using to search through them
I'm searching through them with this script
$tagItems = [];
foreach($tags['items'] as $item) {
if (!$item['snippet']['tags'] || !is_array($item['snippet']['tags'])) {
continue;
}
foreach($item['snippet']['tags'] as $tag) {
$tag = strtolower($tag);
if (!isset($tagItems[$tag])) {
$tagItems[$tag] = 0;
}
$tagItems[$tag]++;
}
}
But let's say I didn't want it to include the 8th array and the 15th array
$tags['items'][8]['snippet']['tags'];
$tags['items'][15]['snippet']['tags'];
I want these to be removed from the original $tags array. How can i achieve this?
EDIT: This needs to be dynamic. I do not know if there are going to be 45/50 arrays that will need removing or just 2/50. the array that needs removing can be reffered to as $index
I have a script which determines what array(s) need to be removed
$i = 0;
while ($i <= 50) {
$x = 0;
while ($x <= 50) {
if ($tags['items'][$i]['snippet']['channelId'] == $tags['items'][$x]['snippet']['channelId']) {
if ($x < $i) {
break;
} else {
echo $x.", ";
break;
}
}
$x++;
}
$i++;
}
I'm going to edit this a little more to provide some extra information that may be useful. My overall goal is to use the YouTube API to remove all but the first array of tags where the channel id appears multiple times. I'm using a script which finds all the array numbers that dont need to be removed an URL.
You can check for the array key
$tagItems = [];
$toRemove = array(8,15);
foreach($tags['items'] as $key => $item) {
if(in_array($key,$toRemove)){
continue;
}
if (!$item['snippet']['tags'] || !is_array($item['snippet']['tags'])) {
continue;
}
foreach($item['snippet']['tags'] as $tag) {
$tag = strtolower($tag);
if (!isset($tagItems[$tag])) {
$tagItems[$tag] = 0;
}
$tagItems[$tag]++;
}
}

Compare differences in Multidimensional array

I have a rather ugly query, and the results from the query are then post-processed using php which turns each row into it's own multidimensional array.
I want to refactor the query but need to make sure I do not change what it returns in any way.
So What I want to do is copy the original query and call that, store the results.
then run the function again with my new query.
Loop over the two arrays of results and compare them for any differences what so ever (keys, values, missing entries, type differences etc).
What is the easiest way to do this?
Essentially I know how to call the two queries etc,
I guess my real question is, at the end once I have my two arrays of the results how do I go through and compare them.
What I would love to end up with is a side by side "print_r" type output with a red line or similar going across highlighting any differences.
First of all, you can use array_uintersect_assoc() like this.
// First get intersecting values
$intersect = array_uintersect_assoc($expected, $results, "checkStructure");
print_r($intersect);
//Then print results that are in intersecting set (e.g. structure of $expected, value of $results
print_r(array_uintersect_assoc($results, $intersect, "checkStructure"));
function checkStructure($x, $y) {
if (!is_array($x) && !is_array($y)) {
return 0;
}
if (is_array($x) && is_array($y)) {
if (count($x) == count($y)) {
foreach ($x as $key => $value) {
if(array_key_exists($key,$y)) {
$x = checkStructure($value, $y[$key]);
if ($x != 0) return -1;
} else {
return -1;
}
}
}
} else {
return -1;
}
return 0;
}
If still not, take help of array_diff()
and array_diff_assoc(). Or try following code.
function multidimensional_array_diff($a1,$a2)
{
$r = array();
foreach ($a2 as $key => $second)
{
foreach ($a1 as $key => $first)
{
if (isset($a2[$key]))
{
foreach ($first as $first_value)
{
foreach ($second as $second_value)
{
if ($first_value == $second_value)
{
$true = true;
break;
}
}
if (!isset($true))
{
$r[$key][] = $first_value;
}
unset($true);
}
}
else
{
$r[$key] = $first;
}
}
}
return $r;
}
Why don't you just make a VIEW that turns an ugly query into something you can just SELECT against? What you're talking about is making a materialized view, something that MySQL doesn't handle as well as other database platforms.
Why not write the results of each query out to a text files then compare the two text files with the diff command?

break; vs continue; vs return;

I downloaded a free newsletter written in php from hotscripts.com
I updated a little the code to add new functionality and I saw something I don't understand.
In the code I see:
foreach() { ...
if() ...
break;
elseif() ...
continue;
}
I also saw:
function() {
// ...
for($nl = 0; ...
if() ...
return true;
}
I read that break; will stop the loop, continue will skip the loop to the next iteration and return will exit the function.
What I don't understand is why coding that style? Why not use something like:
function() {
// ...
for($nl = 0; ...
if() ...
$returnValue = true;
else {
$returnValue = false;
}
}
return $returnValue;
}
or same idea in the for loops?
Using keywords such as break and continue can make the code far easier to read than what you propose.
Especially if you are nesting if/else-statements more than one level.
Compare the snippets further down in this post, which one is easier to read?
Both of them output the same exact thing, and $A is array (1,2,4,4,3,4).
A return in a loop (inside a function) can save precious CPU cycles, if you know you don't need to loop any further, why do it?
I'm too cool to use break/continue..
$not_even_found = false;
foreach ($A as $v) {
if ($v != 1) {
if ($not_even_found) {
} else if ($v % 2 != 0) {
$not_even_found = true;
} else {
echo "$v\n";
}
}
}
I want to have readable code..
foreach ($A as $v) {
if ($v == 1)
continue;
if ($v % 2 != 0)
break;
echo "$v\n";
}
You code using that style so that you save unnecessary loops when the first time a condition is met then you already now that something is true and you don't need to investigate further.
In fact if you return you stop the loop. A stupid example
function in_array($needle, $haystack){
for($i = 0; $i < count($haystack); i++){
if($needle === $haystack[$i]{
return $i;
}
}
return -1;
}
in this case when the condition is met you return something (true, or in this case the value of the counter) because you don't need to iterato over the whole array
Some of the "why" comes down to personal preference. Some of it comes down to whether other things need to be done in the same function before it returns. If there's nothing else that needs to be done, and the function is simply supposed to return a true or false answer, then directly using return true; is the most expedient mechanism. If you still need to do some other stuff even after deciding whether to return true or not (close a file handle for example), then assigning the response to a variable and then returning that at the end may be necessary.

Php index of element in the array, update element

for some reason $post is always < 0. The indoxOf function works great. I use it on ohter codes and it works great
for some reason even after I add the element like this array_push($groups, $tempDon); on the next loop i continues to return -1
$donations = $this->getInstitutionDonations($post->ID);
$groups=array();
foreach( $donations as $don ) : setup_postdata($don);
$pos = $this->indexOf($don, $groups);
print_r($pos);
if($pos < 0)
{
$tempDom = $don;
$tempDon->count = 1;
array_push($groups, $tempDon);
}
else
{
$tempDom = $groups[$pos];
$tempDon->count++;
array_splice($tempDon);
array_push($groups, $tempDon);
echo '<br><br><br>ahhhhhhhhhh<br><br>';
}
endforeach;
protected function indexOf($needle, $haystack) { // conversion of JavaScripts most awesome
for ($i=0;$i<count($haystack);$i++) { // indexOf function. Searches an array for
if ($haystack[$i] == $needle) { // a value and returns the index of the *first*
return $i; // occurance
}
}
return -1;
}
This looks like an issue of poor proofreading to me (note $tempDom vs $tempDon):
$tempDom = $don;
$tempDon->count = 1;
array_push($groups, $tempDon);
Your else block has similar issues.
I also completely agree with #hakre's comment regarding syntax inconsistencies.
EDIT
I'd also like to recommend that you make use of PHP's built-in array_search function in the body of your indexOf method rather than rolling your own.

Search for a specific string within another string with PHP

I need to search a string for any occurances of another string in PHP. I have some code that I've been playing with, but it doesn't seem to be working.
Here is my code:
while (list($key, $val) = each($keyword)) {
$pos = strpos($location, $val);
if($pos == false) {
echo "allow";
exit;
} else {
echo "deny";
exit;
}
}
I have tried some of the options below, but it still does not find the match. Here is what I'm searching:
I need to find:*
blah
In:
http://blah.com
Nothing seems to find it. The code works in regular sentences:
Today, the weather was very nice.
It will find any word from the sentence, but when it is all together (in a URL) it can't seem to find it.
When checking for boolean FALSE in php, you need to use the === operator. Otherwise, when a string match is found at the 0 index position of a string, your if condition will incorrectly evaluate to true. This is mentioned explicitly in a big red box in the php docs for strpos().
Also, based on a comment you left under another answer, it appears as though you need to remove the exit statement from the block that allows access.
Putting it all together, I imagine your code should look like this:
while (list($key, $val) = each($keyword)) {
$pos = strpos($location, $val);
if($pos === false) { // use === instead of ==
echo "allow";
} else {
echo "deny";
exit;
}
}
Update:
With the new information you've provided, I've rewritten the logic:
function isAllowed($location) {
$keywords = array('blah', 'another-restricted-word', 'yet-another-restricted-word');
foreach($keywords as $keyword) {
if (strpos($location, $keyword) !== FALSE) {
return false;
}
}
return true;
}
$location = 'http://blah.com/';
echo isAllowed($location) ? 'allow' : 'deny';

Categories