How to check whether an array of items contains only this item? - php

$items1 = ['apple', 'tree', 'juice'];
$items2 = ['apple', 'tree'];
$items3 = ['apple'];
// loop
If ($items[i].containsOnly['apple'])
{
// do something..
}
In the simplified example above I want to get the array that matches the given item. Is there a method available similar to 'containsOnly'? Or what is the best way to do this?

//If the array has the only item present
if(in_array('apple',$item) && count($item)==1)
{
//Do Something
}

Couple your logic with count:
function containsOnly($a, $v)
{
return count($a) === 1 && array_values($a)[0] === $v;
}
This will ensure that you have only one item and the value is equal to what you are searching for.
Note: The usage of array_values here is to reset all the indexes so we can ensure [0] is where the value will be. Instead of the array_values variation you can use in_array if you prefer that.

You could create a collection of item groups, ->filter() by the condition, then run code on ->each item group which passed the condition.
$itemGroups[] = ['apple', 'tree', 'juice'];
$itemGroups[] = ['apple', 'tree'];
$itemGroups[] = ['apple'];
collect($itemGroups)
->filter(function($items, $key) {
return count($items) == 1 && in_array('apple', $items);
})
->each(function($items, $key) {
// do something
});

You can use the method contains. From the Laravel documentation:
$collection = collect(['name' => 'Desk', 'price' => 100]);
$collection->contains('Desk');
// true
$collection->contains('New York');
// false
I think the example is fairly simple. In your example it would look something like:
$collection = $items1->merge($items2)->merge($items3)
if($collection->contains('apple') && $collection->count() === 1){
// it contains apple
}
Not tested tho but you get the idea behind it.

try this:
$items=array_merge($items1,$items2,$items3);
if (in_array("apple", $items)){
echo "success";
}

Related

link a value from an array to another in PHP

I'm trying two combine two arrays based on the values names.
Here what I'm trying to do :
$array1 = ("fifa21","pes21","halflife2","carma2");
$array2 = ("fifa21_cover","pes21_cover","halflife2_cover");
I want to loop through both arrays and if value from array1 match value from array 2 it will list both values like this :
fifa21 - fifa21_cover
pes21 - pes21_cover
halflife2 - halflife2_cover
carma2 - default_cover
if not find anything in the array2, a default cover will be used.
Note: the arrays will be filled dynamically based in a scanDir (two different folders search) and the arrays can be sorted differently than shown in the example above.
This code will loop through array1 and check to see if the item cover exists in array2, if it does then it will echo out in the format you wanted.
$array1 = array("fifa21","pes21","halflife2","carma2");
$array2 = array("fifa21_cover","pes21_cover","halflife2_cover");
foreach ($array1 as $item) {
if (in_array($item."_cover", $array2)) {
echo "<p>".$item." - ".$item."_cover</p>\n";
} else {
// use default cover...
}
}
(because it is lunch time and I need to write some code)
Given two arrays:
$array1 = ["fifa21", "pes21", "halflife2", "carma2"];
$array2 = ["fifa21_cover", "pes21_cover", "halflife2_cover"];
A function such as this can walk through and look for items that start with those values:
function merge_arrays(array $array1, array $array2): array
{
$output = [];
array_walk(
$array1,
static function ($key) use ($array2, &$output) {
$items = array_filter($array2, static fn($f) => 0 === mb_strpos($f, $key));
if (count($items) === 1) {
$output[$key] = reset($items);
return;
}
if (count($items) === 0) {
$output[$key] = 'default_cover';
return;
}
throw new RuntimeException('More than one item found starting with key ' . $key);
}
);
return $output;
}
When called as:
print_r(merge_arrays($array1, $array2));
It will output:
(
[fifa21] => fifa21_cover
[pes21] => pes21_cover
[halflife2] => halflife2_cover
[carma2] => default_cover
)
For your use-case, it might be overly complicated when compared to a for loop, but it does the job.
Another version that more-blindly assumes only a one-to-one match could use the ?? operator for a null check, although I wouldn't recommend this. The more terse and compact that code looks, the more I find that it is usually very hard to reason about, especially in the future:
function merge_arrays(array $array1, array $array2): array
{
$output = [];
array_walk(
$array1,
static function ($key) use ($array2, &$output) {
$output[$key] = array_filter($array2, static fn($f) => 0 === mb_strpos($f, $key))[0] ?? 'default_cover';
}
);
return $output;
}
All code snipping provided works, however if the array values have extension it always assumes the default cover. I can figure it how using my code by removing the extension for compare purpose, and then I add it back.
Inside my loop I use this :
$keyvaluetemp = substr($keyvalue, 0, strrpos($keyvalue, "."));
the above it inside my first ForEach (where I check the first array), and then in the covers array I remove the "_cover" by nothing :
$keyvalue2temp = (str_replace("_cover", "",$keyvalue2));
then I use the following code inside the second ForEach to add the data to a DB:
// if fantart match the game file name so we associate it to the game and save to the DB
if ( stripos ( $keyvalue2temp , $keyvaluetemp ) !== FALSE )
{
$sql = "UPDATE listgames SET FanArt='$keyvalue2' WHERE Name='$keyvalue'";
$con->exec($sql);
}
// if fanart is empty we add a default picture
$checkifempty = "SELECT `FanArt` FROM `listgames` WHERE `Name` = '$keyvalue'";
$runsql = $con->query($checkifempty);
$row = $runsql->fetch(PDO::FETCH_ASSOC);
$fanartkey = $row['FanArt'];
if($fanartkey == '')
{
$sql = "UPDATE listgames SET FanArt='nopic.JPG' WHERE Name='$keyvalue'";
$con->exec($sql);
}
Later in my code I print the results from the DB. I don't know if this is the best approach, however it does exactly what I want :)

Deleting rows in callback function in array_walk()

I'm trying to work with array using array_walk() function such way:
<?php
$array = array('n1' => 'b1', 'n2' => 'b2', 'n3' => 'b3');
array_walk($array, function(&$val, $key) use (&$array){
echo $key."\n";
if ($key == 'n1')
$val = 'changed_b1';
if ($key == 'n2' || $key == 'n3') {
unset($array[$key]);
}
});
print_r($array);
Get:
n1
n2
Array
(
[n1] => changed_b1
[n3] => b3
)
It seems, what after deletion of 2nd element -- 3rd element don't be sended to callback function.
Use array_filter:
<?php
$filtered = array_filter($array, function($v,$k) {
return $k !== "n2" && $k !== "n3";
}, ARRAY_FILTER_USE_BOTH);
?>
See http://php.net/array_filter
What you can do is use a secondary array, which will give the effect that those nodes have been deleted, like;
<?php
$array = array('n1' => 'b1', 'n2' => 'b2', 'n3' => 'b3');
$arrFinal = array();
array_walk($array, function($val, $key) use (&$array, &$arrFinal){
echo $key."\n";
if ($key == 'n2' || $key == 'n3') {
//Don't do anything
} else {
$arrFinal[$key] = $val;
}
});
print_r($arrFinal);
https://eval.in/206159
From the documentation:
Only the values of the array may potentially be changed; its structure cannot be altered, i.e., the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.
May be that's the reason why you don't get the desired output with your code. Hope it helps.
Possible alternatives:
If you still want to use array_walk(), simply create a new array and copy the 'required' elements i.e. with indices you don't want to delete into the new array. This is a preferred alternative if number of elements to be deleted are very large.
You could look into array_filter or array_map, both rely on applying a callback to every element of your array. You could simply put a condition there barring the indices you want to delete in this callback function. This would work if the number of elements you want to delete are very few.
If however, the elements to delete are contiguous and form a 'portion' of an array (in your case you wanted to remove n2 and n3 which are adjacent). You can use the function array_splice
Sidenote - I am refraining from putting in any code snippets as I have linked the relevant documentations and getting started with them should be a good exercise in itself.
I realize this is several years old, but I found the thread while looking for the solution to a similar situation.
I wound up using preg_grep to return only the array that I wanted to walk, in case anyone finds it useful.
So, in my case, I wanted to ignore files in a scandir array with a "." prefix (system files), then apply a new prefix to the remaining files.
Here's what I wound up with:
$fl = array_map(function($i){
return $new_prefix . "/" . $i;
}, preg_grep("/^[^\.]/", scandir($path)));
If you can build a regex to exclude the undesired array elements, this solution should work.
check the "array_walk" source code and u will see. array_walk use pos to fetch item, and in every loop, the pos move forward.
do {
/* Retrieve value */
zv = zend_hash_get_current_data_ex(target_hash, &pos);
/* Retrieve key */
zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
/* Move to next element already now -- this mirrors the approach used by foreach
* and ensures proper behavior with regard to modifications. */
zend_hash_move_forward_ex(target_hash, &pos);
/* Back up hash position, as it may change */
EG(ht_iterators)[ht_iter].pos = pos;
and reset pos to get value
/* Reload array and position -- both may have changed */
if (Z_TYPE_P(array) == IS_ARRAY) {
pos = zend_hash_iterator_pos_ex(ht_iter, array);
target_hash = Z_ARRVAL_P(array);
} else if (Z_TYPE_P(array) == IS_OBJECT) {
target_hash = Z_OBJPROP_P(array);
pos = zend_hash_iterator_pos(ht_iter, target_hash);
} else {
php_error_docref(NULL, E_WARNING, "Iterated value is no longer an array or object");
result = FAILURE;
break;
}
if key= n1; then next pos 1
if key= n2; then next pos 2
if key= n3; then next pos 3
when run [$key == 'n2'] , the next pos is 2 ; after unset , pos 2 is unreachable ,so the loop end.
so in actually ,then $key=='n3' will not happen, and u will get the result.

php - Find array for which a key have a given value

I have an array of dictionnaries like:
$arr = array(
array(
'id' => '1',
'name' => 'machin',
),
array(
'id' => '2',
'name' => 'chouette',
),
);
How can I find the name of the array containing the id 2 (chouette) ?
Am I forced to reindex the array ?
Thank you all, aparently I'm forced to loop through the array (what I wanted to avoid), I thought that it were some lookup fonctions like Python. So I think I'll reindex with id.
Just find the index of array that contains the id you want to find.
SO has enough questions and answers on this topic available.
Assuming you have a big array with lots of data in your real application, it might be too slow (for your taste). In this case, you indeed need to modify the structure of your arrays, so you can look it up faster, e.g. by using the id as an index for the name (if you are only interested in the name).
As a for loop would be the best way to do this, I would suggest changing you array so that the id is the arrays index. For example:
$arr = array(
1 => 'machin',
2 => 'chouette',
);
This way you could just get the name for calling $arr[2]. No looping and keeping your program running in linear time.
$name;
foreach ($arr as $value){
if ( $value['id'] == 2 ){
$name = $value['name'];
break;
}
}
I would say that it might be very helpful to reindex the information. If the ID is unique try something like this:
$newarr = array();
for($i = 0;$i < count($arr);$i++){ $newarr[$arr[$i]['id']] = $arr[$i]['name']; }
The result would be:
$newarr = array('1'=>'machin','2'=>'chouette');
Then you can go trough the array with "foreach" like this:
foreach($newarr as $key => $value){
if($value == "machin"){
return $key;
}
}
But of course the same would work with your old array:
foreach($arr as $item){
if($item['name'] == "machin"){
return $item['id'];
}
}
It depends on what you are planning to do with the array ;-)
array_key_exist() is the function to check for keys. foreach will help you get down in the multidimensional array. This function will help you get the name element of an array and let you specify a different id value.
function findKey($bigArray, $idxVal) {
foreach($bigArray as $array) {
if(array_key_exists('id', $array) && $array['id'] == $idxVal) {
return $array['name'];
}
}
return false;
}
//Supply your array for $arr
print(findKey($arr, '2')); //"chouette"
It's a bit crude, but this would get you the name...
$name = false;
foreach($arr as $v) {
if($v['id'] == '2') {
$name = $v['name'];
break;
}
}
echo $name;
So no, you are not forced to reindex the array, but it would make things easier.

PHP - How to append array with some conditions

$list = array(
[0]=> array(
[name]=>'James'
[group]=>''
)
[1]=> array(
[name]=>'Bobby'
[group]=>''
)
)
I am looking to update the item 'group' where the name is 'Bobby'. I am looking for a solution with the two following formats. Thank you in advance for your replies. Cheers. Marc.
array_push($list, ???)
and
$list[] ??? = someting
As far as I know, there's no way updating your array with one of the given syntax.
The only similar thing I can come on is looping over the array using array_walk ... http://www.php.net/manual/en/function.array-walk.php
Example:
array_walk($list, function($val, $key) use(&$list){
if ($val['name'] == 'Bobby') {
// If you'd use $val['group'] here you'd just editing a copy :)
$list[$key]['group'] = "someting";
}
});
EDIT: Example is using anonymous functions which is only possible since PHP 5.3. Documentation offers also ways working with older PHP-versions.
This code may help you:
$listSize = count($list);
for( $i = 0; $i < $listSize; ++$i ) {
if( $list[$i]['name'] == 'Bobby' ) {
$list[$i]['group'] = 'Hai';
}
}
array_push() doesn't really relate to updating a value, it only adds another value to an array.
You cannot have a solution that will fit both formats. The implicit array push $var[] is a syntactic construct, and you cannot invent new ones - certainly not in PHP, and not most (all?) other languages either.
Aside from that, what you are doing is not pushing an item on to the array. For one thing, pushing items implies an indexed array (yours is associative), and for another pushing implies adding a key to the array (the key you want to update already exists).
You can write a function to do it, something like this:
function array_update(&$array, $newData, $where = array(), $strict = FALSE) {
// Check input vars are arrays
if (!is_array($array) || !is_array($newData) || !is_array($where)) return FALSE;
$updated = 0;
foreach ($array as &$item) { // Loop main array
foreach ($where as $key => $val) { // Loop condition array and compare with current item
if (!isset($item[$key]) || (!$strict && $item[$key] != $val) || ($strict && $item[$key] !== $val)) {
continue 2; // if item is not a match, skip to the next one
}
}
// If we get this far, item should be updated
$item = array_merge($item, $newData);
$updated++;
}
return $updated;
}
// Usage
$newData = array(
'group' => '???'
);
$where = array(
'name' => 'Bobby'
);
array_update($list, $newData, $where);
// Input $array and $newData array are required, $where array can be omitted to
// update all items in $array. Supply TRUE to the forth argument to force strict
// typed comparisons when looking for item(s) to update. Multiple keys can be
// supplied in $where to match more than one condition.
// Returns the number of items in the input array that were modified, or FALSE on error.

in_array for a combo value ('test','value')

I'm trying to use in_array or something like it for associative or more complex arrays.
This is the normal in_array
in_array('test', array('test', 'exists')); //true
in_array('test', array('not', 'exists')); // false
What I'm trying to search is a pair, like the combination 'test' and 'value'. I can set up the combo to be searched to array('test','value') or 'test'=>'value' as needed. But how can I do this search if the array to be searched is
array('test'=>'value', 'exists'=>'here');
or
array( array('test','value'), array('exists'=>'here') );
if (
array_key_exists('test', $array) && $array['test'] == 'value' // Has test => value
||
in_array(array('test', 'value'), $array) // Has [test, value]
) {
// Found
}
If you want to see if there is a key "test" with a value of "value" then try this:
<?php
$arr = array('key' => 'value', 'key2' => 'value');
if(array_key_exists('key',$arr) && $arr['key'] == 'value'))
echo "It is there!";
else
echo "It isn't there!";
?>
If I understand you correctly, you're looking for a function called array_search()
It accepts a mixed value, so you can even search for objects - I haven't tried it exactly, but it should work for your use case:
if (array_search(array('test','value'), array(array('test','value'),array('nottest','notvalue'))) !== false) {
// item found...
}
ok..
However I think you'll find this method the most useful:
If you just need to find out if a certain key/value pair is located in an array, the easiest way to do it is like this:
<?php
if (isset($arr['key']) && $arr['key'] == 'value') {
// we have a match...
}
?>
if you need to find something in a more complex pattern, there's no avoid creating a bigger loop.
Separate Keys from Values and use in_array()
$myArray = array('test'=>'value', 'exists'=>'here');
array_keys($myArray)
array_values($myArray)

Categories