Check if one of multiple variables exists in array - php

$array = ['a', 'b', 'c', 'd'];
$vars = ['a', 'f', 'g'];
foreach ($vars as $var) {
if (in_array($var, $array)) {
return true;
} else {
return false;
}
}
How can i check if one of the $vars exists in $array? Only one of them needs to be true if all the others are false it is not a problem, But if there is more than 1 true value,
For example ['a', 'b', 'c', 'g'] i want the function to stop at the first true value and ends the process.

Simply with array_intersect function:
$arr = ['a', 'b', 'c', 'd'];
$vars = ['a', 'f', 'g'];
$result = count(array_intersect($vars, $arr)) == 1;
var_dump($result); // true

For me, the code you've got in the question is pretty much the best way to go about this. You just need to move the return false to after the loop, so that all values get processed:
foreach ($vars as $var) {
if (in_array($var, $array)) {
return true;
}
}
return false;
This will work identically to a solution with array_intersect, but has the advantage of only needing to process the minimum amount of the loop. Consider the following data:
$vars = [1, 2, 3, 4, ... 1,000,000,000];
$array = [1, 10, ...];
A solution using array_intersect will need check every element in $vars against every element in $array, whereas a solution that breaks out of a loop will only need to check until the first match. You could optimise this further by using two nested foreach loops if both your arrays are very large.
Like I mentioned in the comment - if your arrays are small then just use array_intersect, you won't notice any difference, and the code is a bit more readable.

Related

How would I identify how many arguments are numerals using variable length parameters in PHP

I am teaching myself php and I came across these variable length functions included in PHP. They are three being
func_get_args ()
func_num_args ()
func_get_arg ($i)
I understand how to use them so far, I would like to know how divide the arguments or arrays to know which ones are strings and which are numerical. for example
$data = funcArgSort('red', 'green', 21, 'blue', 67);
One you get the list of args you can use array_filter() to pull out the arguments that are int's and the ones that are strings.
You can use is_int() and is_string() respectively.
Then if you can merge them back into one array and sort before returning.
For example:
<?php
function funcArgSort() {
$args = func_get_args();
// this gets an array of numbers
$nums = array_filter($args, fn ($x) => is_int($x));
// this gets an array of strings
$strings = array_filter($args, fn ($x) => is_string($x));
// this merges the two arrays
$merged = [...$nums, ...$strings];
// Note that `sort` returns a boolean and mutates the array
// so we don't want to return the result of `sort`
sort($merged);
return $merged;
}
var_dump(funcArgSort('c', 3, 'b', 2, 'a', 1));
// output: ['a', 'b', 'c', 1, 2, 3]
Another alternative would be to use a loop to iterate through each argument and build up a list of numbers and strings. In this way, you only have to loop through the list of arguments once (which may or may not be a performance issue for your needs).
<?php
function funcArgSort() {
$args = func_get_args();
$nums = [];
$strings = [];
// Loop through each argument
foreach($args as $arg) {
// If it's a number, push it onto the nums array
if (is_int($arg)) $nums[] = $arg;
// If it's a string, push it onto the strings array
if (is_string($arg)) $strings[] = $arg;
}
$merged = [...$strings, ...$nums];
sort($merged);
return $merged;
}
var_dump(funcArgSort('c', 3, 'b', 2, 'a', 1));
// output: ['a', 'b', 'c', 1, 2, 3]
And another alternative, if your use-case is as contrived as the examples above you could just sort the args and it will work as such:
function funcArgSort() {
$args = func_get_args();
sort($args);
return $args;
}
var_dump(funcArgSort('c', 3, 'b', 2, 'a', 1));
// output: ['a', 'b', 'c', 1, 2, 3]

how to get array_diff to ignore duplicates in php? [duplicate]

I am using array_diff() to take values out of array1 which are found in array2. The issue is it removes all occurrences from array1, as the PHP documentations makes note of. I want it to only take out one at a time.
$array1 = array();
$array1[] = 'a';
$array1[] = 'b';
$array1[] = 'a';
$array2 = array();
$array2[] = 'a';
It should return an array with one 'a' and one 'b', instead of an array with just 'b';
Just for the fun of it, something that just came to mind. Will work as long as your arrays contain strings:
$a = array('a','b','a','c');
$b = array('a');
$counts = array_count_values($b);
$a = array_filter($a, function($o) use (&$counts) {
return empty($counts[$o]) || !$counts[$o]--;
});
It has the advantage that it only walks over each of your arrays just once.
See it in action.
How it works:
First the frequencies of each element in the second array are counted. This gives us an arrays where keys are the elements that should be removed from $a and values are the number of times that each element should be removed.
Then array_filter is used to examine the elements of $a one by one and remove those that should be removed. The filter function uses empty to return true if there is no key equal to the item being examined or if the remaining removal count for that item has reached zero; empty's behavior fits the bill perfectly.
If neither of the above holds then we want to return false and decrement the removal count by one. Using false || !$counts[$o]-- is a trick in order to be terse: it decrements the count and always evaluates to false because we know that the count was greater than zero to begin with (if it were not, || would short-circuit after evaluating empty).
Write some function that removes elements from first array one by one, something like:
function array_diff_once($array1, $array2) {
foreach($array2 as $a) {
$pos = array_search($a, $array1);
if($pos !== false) {
unset($array1[$pos]);
}
}
return $array1;
}
$a = array('a', 'b', 'a', 'c', 'a', 'b');
$b = array('a', 'b', 'c');
print_r( array_diff_once($a, $b) );

PHP 7 changes to foreach: Can I still delete items in they array on which I'm iterating?

The PHP 7 Backward-Incompatible Changes Document says the following about foreach:
When used in the default by-value mode, foreach will now operate on a copy of the array being iterated rather than the array itself. This means that changes to the array made during iteration will not affect the values that are iterated.
I'm trying to understand what this means, and my main question is whether this code will work the same in PHP 7 as it does in PHP 5.6?
foreach($array as $elementKey => $element) {
if ($element == 'x') {
unset($array[$elementKey]);
}
}
My two questions are:
Will this code still work?
If so, can you explain (maybe by example) what this new change in PHP 7 means?
Edit
I've been re-reading the doc statement. I'm thinking that what it means is that, if you change values of items lower down in the array, those changes won't be there when you get to those items in the iteration. Example:
$array = ['x', 'y', 'z'];
$new = [];
foreach($array as $element) {
if ($element == 'x') {
$array[2] = 'a';
}
$new[] = $element;
}
print_r($new);
However, when I run this example it doesn't seem to show any difference in PHP versions (although I've never used this tool before, so I'm not sure how it works).
I realize that if I do it by reference, I'll get an a in new. Otherwise I won't. But this seems to be the case in both versions.
What I really need to know is what is the incompatibility (by example)?
Edit 2
The answer link suggested by #NikiC provides the rest of the story I was looking for:
In most cases this change is transparent and has no other effect than better performance. However there is one occasion where it results in different behavior, namely the case where the array was a reference beforehand:
$array = [1, 2, 3, 4, 5];
$ref = &$array;
foreach ($array as $val) {
var_dump($val);
$array[2] = 0;
}
/* Old output: 1, 2, 0, 4, 5 */
/* New output: 1, 2, 3, 4, 5 */
Previously by-value iteration of reference-arrays was special cases. In this case no duplication occurred, so all modifications of the array during iteration would be reflected by the loop. In PHP 7 this special case is gone: A by-value iteration of an array will always keep working on the original elements, disregarding any modifications during the loop.
This answer explains the rare "special case" where things work different between versions in regarding foreach operating on a copy of the array.
All it means is that you now have to explicitly say you want to reference the array you're iterating now.
In your example code, however, you're referencing the root array anyways so it will work whether or not you pass by reference.
<?php
$array = ['x', 'y', 'z'];
foreach($array as $elementKey => $element) {
if ($element=='x') {
unset($array[$elementKey]);
}
}
var_dump($array); // lists 'y' and 'z'
A better example. In this case, we're changing the value inside the foreach without a reference. As such, the local changes will be lost:
<?php
$array = ['x', 'y', 'z'];
foreach($array as $element) {
if ($element=='x') {
$element = 'a';
}
}
var_dump($array); // 'x', 'y', 'z'
Vs by reference, where we declare the $element to be a reference to the array element:
<?php
$array = ['x', 'y', 'z'];
foreach($array as &$element) {
if ($element=='x') {
$element = 'a';
}
}
var_dump($array); // 'a', 'y', 'z'
Here's a demo of this code running on various versions of PHP.

Explain how this array transposing and flattening function works

Here this function in PHP that allows to merge any N amount of different length arrays in a fashion that output array will be in next order: Array1[0],Array2[0],..,ArrayN[0],Array1[1],Array2[1],..,ArrayN[1]... :
function array_zip_merge() {
$output = array();
// The loop incrementer takes each array out of the loop as it gets emptied by array_shift().
for ($args = func_get_args(); count($args); $args = array_filter($args)) {
// &$arg allows array_shift() to change the original.
foreach ($args as &$arg) {
$output[] = array_shift($arg);
}
}
return $output;
}
// test
$a = range(1, 10);
$b = range('a', 'f');
$c = range('A', 'B');
echo implode('', array_zip_merge($a, $b, $c)); // prints 1aA2bB3c4d5e6f78910
While I understand what each of built in functions in this example do on their own, I just cant wrap my mind how it all works together in this function, despite included explaining commentaries...
Can someone break it down for me, please? The function works great as is, its just driving me crazy that I don't understand how it works...
P.S: this function is taken from Interleaving multiple arrays into a single array question.
The arrays $a, $b and $c have 10, 6 and 2 elements respectively.
$a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$b = ['a', 'b', 'c', 'd', 'e', 'f'];
$c = ['A', 'B'];
When you feed the arrays as arguments for the array_zip_merge() function, look at the for loop. The func_get_args() will set the $args with all the arguments supplied. On start of first for loop run,
$args = [$a, $b, $c];
count($args) = 3;
At the foreach loop the array_shift will return the first element of each array resulting the $output to be like
$output = [1, 'a', 'A'];
And the arrays now look like,
$a = [2, 3, 4, 5, 6, 7, 8, 9, 10];
$b = ['b', 'c', 'd', 'e', 'f'];
$c = ['B'];
At the end of the first for loop the array_filter function will test if any array is empty and remove it from $args. Same thing will happen at the second run, and by the end of the second for loop, the variables would look like
$a = [3, 4, 5, 6, 7, 8, 9, 10];
$b = ['c', 'd', 'e', 'f'];
$c = [];
$output = $output = [1, 'a', 'A', 2, 'b', 'B'];
//because $c is empty array_filter() removes it from $args
$args = [$a, $b];
So, on the third iteration of the for loop count($args) will return 2. When the last element of $b has been removed by array_shift the count($args) will return 1. The iteration will continue until all the arrays are empty
Inside array_zip_merge, the for statement always takes the first values of each array and add them to output variable respectively.
Because array_shift removes the element it returns, on every loop the first elements are different. When it gets empty because of it, the loop has nothing to do and breaks.
If you still dont understand, ask the specific part of the code you have trouble with please.
The custom function is effectively a transposing and flattening technique that will accommodate a non-matrix set of data. By non-matrix, I mean by pushing the three arrays into a single array, there will be columnar gaps in the data set. Using implode(array_merge(...array_map(null, $a, $b, $c))) will work by happenstance (because you are imploding the data with an empty string as glue, but in other cases, the generation of null elements will potentially skew the results.
I find the borrowed script to be very hard to interpret. The for loop would be much simpler as a foreach(). Instead of calling func_get_args(), the incoming columnar data could have been declared a variable using the spread operator in the function definition.
Here is one way to perform the 3-step task (without implode):
Transpose the data from the 3 arrays (isolate columns of data)
Filter null values out of the columnar data
Flatten the filtered two dimensional array (array_merge(...$tranposedAndfiltered))
Code: (Demo)
var_export(
array_merge(
...array_map(
fn(...$col) => array_filter(
$col,
fn($v) => !is_null($v)
),
$a,
$b,
$c
)
)
);
If it is simpler to perform the transposition without the spread operator and null filtering, just use nested foreach loops before flattening and imploding. (Demo)
$output = [];
foreach ([$a, $b, $c] as $i => $array) {
foreach ($array as $k => $v) {
$output[$k][$i] = $v;
}
}
var_export(implode(array_merge(...$output)));
See Stack Overflow's array transpose canonical: Transposing multidimensional arrays in PHP

php elegance keys unsetting

I need to remove from an array some keys.
$array = array('a' => 'a', 'b' => 'b', 'c' => 'c');
unset($array['a']);
unset($array['b']);
How can I do this more elegance? Maybe there is a function like this array_keys_unset('a', 'b')?I don't need array_values or foreach. I only want to know is it possible.
Thank you in advance. Sorry for my english and childlike question.
You can do that with single call to unset as:
unset($array['a'],$array['b']);
unset() is as simple as it gets, but as another solution how about this?
$keys_to_remove = array_flip(array('a', 'b'));
$array = array_diff_key($array, $keys_to_remove);
Put into a function:
function array_unset_keys(array $input, $keys) {
if (!is_array($keys))
$keys = array($keys => 0);
else
$keys = array_flip($keys);
return array_diff_key($input, $keys);
}
$array = array_unset_keys($array, array('a', 'b'));
Or you could even make it unset()-like by passing it a variable number of arguments, like this:
function array_unset_keys(array $input) {
if (func_num_args() == 1)
return $input;
$keys = array_flip(array_slice(func_get_args(), 1));
return array_diff_key($input, $keys);
}
$array = array_unset_keys($array, 'a', 'b');
Personally, I would just do this if I had a long / arbitrary list of keys to set:
foreach (array('a', 'b') as $key) unset($array[$key]);
You could use a combination of array functions like array_diff_key(), but I think the above is the easiest to remember.
What's wrong with unset()?
Note that you can do unset($array['a'], $array['b']);
You could also write a function like the one you suggested, but I'd use an array instead of variable parameters.
No, there is no predefined function like array_keys_unset.
You could either pass unset multiple variables:
unset($array['a'], $array['b']);
Or you write such a array_keys_unset yourself:
function array_keys_unset(array &$arr) {
foreach (array_slice(func_get_args(), 1) as $key) {
unset($arr[$key]);
}
}
The call of that function would then be similar to yours:
array_keys_unset($array, 'a', 'b');

Categories