how can a find multiple values at once in an array php - php

trying to make a tic-tac-toe game actually. So if there's a better way I would like to know too!
So I have a grid [0,8], and the user's positions are stored in an array. Great.
Next I want to see if the columns match up. So let's say first row is (0,1,2).
My question is if the user has an array like array(1,5,6,0,2), so he won. How can I match it efficiently?
I was thinking of doing something like this but doesn't look efficient:
$user= array(1,5,6,0,2);
in_array(0,$user) && in_array(1,$user) && in_array(2,$user)
I also thought about switch case but still face the question how do I find multiple values at once.

I agree that with such a small number of possible combinations to win, the solution can be kept very simple. I would do it using array_diff.
function has_won($user) {
// returns false if the user has not won, otherwise
// returns the first winning combination found.
$wins = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];
foreach ($wins as $win) {
if (!array_diff($win, $user)) return $win;
// array_diff returns the values in the first argument
// not present in any subsequent arguments. So if its
// result is empty, the user has winning combination.
}
return false;
}

I will not make a claim that this is the most efficient method, but it reduces the number of operations considerably compared with && chaining and multiple in_array() calls.
What we'll do is keep a 2-dimensional array of possible win-combinations: rows, columns, diagonals, as sub-arrays. Then in a foreach loop over that 2D array, test the user's current array state against the row combination with array_intersect(). If the entire winning combination is present in the user's array, the result of array_intersect() will be 3, which you can test with count().
Since one match is enough for a win, you can break out of the loop on the first match to declare a win.
$combinations = array(
// Rows
array(0,1,2),
array(3,4,5),
array(6,7,8),
// Columns
array(0,3,6),
array(1,4,7),
array(2,5,8),
// Diagonals
array(0,4,8),
array(2,4,6),
);
// Loop over the array of winners and test array_intersect()
// If 3 values intersect, the full win combination was matched
foreach ($combinations as $combination) {
if (count(array_intersect($combination, $user)) == 3) {
// User wins! Take whatever action necessary...
// Exit the loop
break;
}
}
Here's a demonstration in which 2 of 3 sets for $user are winners: http://codepad.viper-7.com/Mvu0wa
There are algorithmic ways of generating the winning combinations rather than hard-coding them, but there are only eight possible combinations so it isn't that hard, and the point here is the use of array_intersect() to find a subset of the user's current placements.

Related

Does array_values in PHP loop through all the items?

I want to know if inbuilt PHP array functions such as array_diff, array_keys or array_values (in comparison to array_walk) iterate through each item or do they have an internal algorithm through which they do the computation in one go?
This is important when I want to learn how to optimise PHP scripts which handle 100,000 items.
For e.g. this method:
public function narrowDown($BigArray, $Column, $regex)
{
# narrowDown to focus on columns with similar data
$Column = array_column($BigArray, $Column);
$Search = preg_quote($regex, '~');
$Matched = preg_grep('~'.$Search.'~', array_combine(array_keys($BigArray), $Column));
# recreate rows by intersecting with specified keys
return array_intersect_key($BigArray, $Matched);
}
This method finds out similar rows in a specified column by regex in a multi-dimensional array.
The array has 18 columns and 100,000 items. I was thinking what should be the best way to optimise such methods.
Feel free to also advise if I should shift to a different programming language.
Yes, they iterate through all items, also calls and their results are not cached in any way.
So if you will call array function twice with exactly same input, all the work will be done twice.

PHP: return length of each array Element or stop

given I have an array, say:
$myArray=['12','AB','3C']
I want to return the value 2 (which is the length of each of the array elements indivudually.)
But in case I have something like
$myArray=['12','AB2','3C']
I want to stop the calculation/loop right after the second element of the array 'AB2' and let my function return null.
What is the most effective way to reach this in the matter of being performance and speed effective? Since such an array can get long.
Casual way
I think you are trying to stop the array loop the moment you get two different lengths in an element?
In that case, at worst, you'd need an O(n) runtime (since you need to verify every element, unless you have an abstract data type in mind in which case it could be O(1), if it is stored in the object property or you calculate the difference detected on the fly while pushing items into arrays)
Since the moment we discover an element is not the same length, we can simply quickly store the length of the first element in the array since we know if we detect any other length other than what we stored, we can immediately return null
function linear_loop($array) {
$len_of_first = strlen($array[0]);
foreach ($array as $val) {
if (strlen($val) != $len_of_first) {
return null;
}
}
//Function still running, entire array was same, return the length of first element
return $len_of_first;
}
This function is O(n) with each operation is constant. strlen is O(1)
Algorithmic complexity of PHP function strlen()
Most "performance-fastest"
Since you said that the array can get quite long, if you are not immediately generating the array, but rather you need to push items into it, then in your push operation, you can check before pushing it the item_to_be_pushed is the same strlen or whatever property you are trying to compare as the one you've stored (which can be picked arbitrarily, since the array must be of uniform some property)
In this case, you could have some object with property: uniform_length and store that. Then whenever you push into your array, you can check against it with the uniform_length. If it isn't the same length, then you can store in the object property called uniform as false. (By default uniform is true since if there is only one element in the array, it must be uniform).
This would be an O(1) calculation since it is stored as an attribute. But you probably don't need an object for something as simple as this, and you can just store it as some variable.
O(1) vs O(n) Runtime and why it is more performance effective
Since not everyone knows Big O, a quick explanation on what I said. O(1) runtime is "infinitely" better than O(n) runtime since the runtime of the function will not grow with input (as in processing 1 million items require the same amount of steps as processing 1 item)
Just loop through and return early when you find something that isn't correct. Don't worry about micro-optimizations until you have profiled and found that this function is really your bottleneck
ex.
function isCorrect($arr) {
$len = strlen($arr[0]);
for($arr as $val) {
if(strlen($val) != $len) {
return false;
}
}
return true;
}
Just my two cents. You could also use array_map for this:
$myArray = ['12','AB','3CC'];
$lengths = array_map('strlen', $myArray);
// output
Array
(
[0] => 2
[1] => 2
[2] => 3
)
you can just write an if statement and check the max($lengths) and return true or false
-Cheers

Testing returned values for duplicates

I've got a method that returns a string with predefined length of random alpha numbers.
I just let it run until it comes across a duplicate and breaks out of the loop and display the amount of generations it took before a duplicate was generated.
The loop numbers will be in the millions, and I am wondering if this really is good and efficient of testing it?
You can use array_search
The function return the string and store it to an array.
Then use array_search to see if it is not FALSE and loop.
Put The generated strings into an array as a key before outputting them.
$array[$alpha_string] = (isset($array[$alpha_string])) ? $array[$alpha_string] + 1 : 1;
Then check which items have a count > 1.

when selecting random values from arrays, make sure two certain ones do not appear next to each other

I have an array with 18 values in it from which I select random values using $array[rand(0,17)]. I put these randomly selected values next to each other on the page. Within the array are 6 sets of values that I do not want to be put next to each other on the page. Is there any way that I can detect when the pairs are together and select new values because of that
warning: Do you know for sure that you won't get any degenerate cases where there are no possible orderings of the array? For example, if you won't allow the pairs [1,2] or [2,1] and the array you get is [1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2], the you're out of luck. There's no way to display the array in the way you want, and a method like I describe below will never terminate.
I would use shuffle($array) and then iterate through the shuffled array one item at a time, to find out whether any value is "incompatible" with the item before it. If so, just reshuffle the array and try again. You can't predict how many tries it will take to get a shuffled array that works, but the amount of time it takes should be negligible.
To detect whether two values are compatible, I'd suggest making an array that contains all incompatible pairs. For example, if you don't want to have the consecutive pairs 1 and 3 or 2 and 5, then your array would be:
$incompatible = array(
array(1,3),
array(2,5) );
Then you'd iterate over your shuffled array with something like:
for ($i=1; i<count($array)-1; i++;) {
$pair = $array[i, i+1]; // this is why the for loop only goes to the next-to-last item
if in_array($pair, $incompatible) {
// you had an incompatible pair in your shuffled array.
// break out of the for loop, re-sort your array, and try again.
}
}
// if you get here, there were no incompatible pairs
// so go ahead and print the shuffled array!
Or use with unset() for remove the keys, or use by Session, for next skip.

PHP - Not getting the element I expect from an array

I am paging through the elements of an array.
I get the total number of elements in the array with:
$total = count($myarray);
My paging function loads the current element on the page and provides "Previous" and "Next" links that have urls like:
http://myapp.com?page=34
If you click the link I grab that and load it onto the page by getting (I sanitize the $_GET, this is just for example):
$element = $myarray[$_GET['page']];
This should grab the element of the array with a key == $_GET['page'] and it does. However, the problem is that my total count of elements doesn't match some keys because while there are 100 elements in the array, certain numbers are missing so the 100th item actually has a key of 102.
How should I be doing this? Do I need to rewrite the keys to match the available number of elements? Some other method? Thanks for your input.
If you have gaps in the indices, you should reindex the array. You can do that before you generate the links, or probably easier on the receiving page:
$myarray = array_values($myarray);
$element = $myarray[$_GET['page']];
This would give you the 100th element, even if it previously had the key 102. (You could use a temporary array of course, if you need to retain the original indexing.)
you can use
$array = array_values($array);
How should I be doing this? Do I need
to rewrite the keys to match the
available number of elements? Some
other method?
No you don't need to worry about them not matching. Php arrays are associative containers, like dictionaries in other languages. If you define something at 98 and 100, 99 isn't sitting there in memory, the data structure behind the associative container only stores whats there. You're not wasting space by not "filling it up" up to count.
So the practice you describe is fine. If there is no page "99" nothing need show up in your array. It may be nice, however, to see that your array doesn't have anything for the 'page' parameter and display an error message.
But then why, when I access $total =
count($myarray); $myarray[$total]
where $total = 100 I do not get the
last element? I can put page=101 and
get one more record. I should not be
able to do this
Because count is counting how many things are in the array. If you have an array with only the even elements filled in, ie:
$myArray[0] = "This";
$myArray[2] = "is";
$myArray[4] = "even";
Here count($myArray) is 3. There's nothing in [1] or [3]. Maybe this is easier to see when you take numbers out of the equation. Arrays can have string indexes
$myArray = array();
$myArray["Hello"] = "A Bunch of";
$myArray["World"] = "words";
Here count($myArray) is 2.
In the first case, it wouldn't make sense to access $myArray[3] because nothing is there. Clearly in the second example, there's nothing at 2.

Categories