I have an multidimensional array with objects. If the object has status 1 or 2 and the color is not green, I want to set color to blue. How can I do that?
foreach ($objects as $obj) {
if (in_array($obj['status'], [1,2] )) {
if ($obj['color'] != 'green'){
//set color to blue
}
}
}
No need to have nested ifs, just combine them into one condition, and you can reference the copy & to make your changes:
foreach ($objects as &$obj) {
// ^ reference
if (in_array($obj['status'], [1,2]) && $obj['color'] != 'green') {
// set to blue
$obj['color'] = 'blue';
}
}
The answer provided by #Ghost is correct when working with arrays. As arrays are not really objects they must be passed by reference otherwise they are copied. Objects are passed by reference.
This is actually pretty horrible, and one of the (many) reasons you should avoid basic PHP arrays. Additionally, if you make a reference of a reference, you'll likely end up with a memory leak.
If $objects where actually an array of actual objects, you don't need to specify that it should be treated as an exception. Here's a test I wrote (sorry it's a bit ugly):
$objects = array(
(object)array(
'status' => 1,
'color' => 'red',
)
);
foreach ($objects as $obj) {
if (in_array($obj->status, array(1,2)) && $obj->color != 'green') {
// set to blue
$obj->color = 'blue';
}
}
foreach ($objects as $obj) {
echo $obj->color;
}
For the most part, PHP objects are faster and more memory efficient that PHP arrays, and will act very similarly in most cases.
Related
I'm not even sure how to approach the problem so I'm just stating the problem. Any help is highly appreciated.
There's this array ($colors) of all possible values:
$colors = array ('red','blue','green','yellow');
Then there's an array ($boxes) of all the possible values - consisting of equal number of values as $colors:
$boxes = array ('circular','squared','hexagonal','triangular');
There's this third array which defines the constraints in making the unique combination:
$possible_combos = array
(
array('circular','red','blue'),
array('squared','red','green'),
array('hexagonal','blue','yellow'),
array('triangular','red','green')
);
Question: How do I get a new array $result with key=>value combinations just that each box is assigned a unique color out of it's possible sets of colors
So a valid $result array would be:
Array ( [circular] => red
[squared] => blue
[hexagonal]=> yellow
[triangular] => green
)
NOTE: If you traverse sequentially, 'red', 'blue', 'green' might get assigned to first three 'boxes' and there might not be anything to pick for the fourth box (since 'yellow' is not allowed to be assigned to it.
I'm thinking, to process the least occurring 'colors' first but really unsure how to handle it syntactically.
Once again thanks for looking into it, in advance! Even some help with how to approach the problem would be nice.
Following code is not producing the correct output either:
foreach ($colors as $c => $color) {
foreach ($boxes as $b => $box) {
for ($i=0; $i < count($colors) ; $i++) {
if(in_array($possible_combos[$i][1],$colors) && !in_array($possible_combos[$i][1], $result))
{
$result[$box] = $possible_combos[$i][1];
unset($colors[$c]);
break;
}
else if(in_array($possible_combos[$i][2],$colors) && !in_array($possible_combos[$i][2], $result))
{
$result[$box] = $possible_combos[$i][2];
unset($colors[$c]);
break;
}
}
}
}
I'm thinking, to process the least occurring 'colors' first but really unsure how to handle it syntactically.
That's a good intuition considering the fact that you have only one box type that can share one of many colors. Because your constraining resource is color, I think it makes sense to sort your rules by them first and then you can distribute by scarcity.
https://3v4l.org/u5pBK
$colors = array ('red','blue','green','yellow');
$boxes = array ('circular','squared','hexagonal','triangular');
$possible_combos = array
(
array('circular','red','blue'),
array('squared','red','green'),
array('hexagonal','blue','yellow'),
array('triangular','red','green')
);
// collect constraints ordered by rarest
foreach ($possible_combos as $constraint) {
$box = array_shift($constraint);
foreach ($constraint as $color) {
$constraints[$color] []= $box;
}
}
// assign rarest first to last
asort($constraints);
foreach ($constraints as $color => $allowedBoxes) {
foreach ($allowedBoxes as $box) {
$key = array_search($box, $boxes);
// if we have a match, then remove it from the collection
if ($key !== false) {
$result[$box] = $color;
unset($boxes[$key]);
continue 2;
}
}
}
print_r($result);
Array
(
[hexagonal] => yellow
[circular] => blue
[squared] => green
[triangular] => red
)
(examples at the bottom!!!)
we have just upgrade our backend to PHP7 and after that, we have found a bug in our code related to an ArrayObject.
The code just loops over a copy of an Object (type native ArrayObject). The foreach iterates by value.
The purpose of the code is to filter some values that you don't need. In the example, if the iterated value is "two" or "three", unset it. I have tried it using iterator instead of the copied value and without the iterator.
Results:
- Iterator
PHP 5.6: works as expected, the returned value is the array without the values "two" and "three"
PHP 7: it only removes "two" and seems that the item with value "three" is not evaluated (see echo inside the loop, it doesn't print "three")
- No Iterator
PHP 5.6: gets a notice but works as expected, the returned value is the array without the values "two" and "three"
PHP 7: it only removes "two" and seems that the item with value "three" is not evaluated (see echo inside the loop, it doesn't print "three")
First loop => $key = 0, $value = "one" // continue
Second loop => $key = 1, $value = "second" // unset
Third loop => $key = 3, $value = "four" // WTF? where is the $key = 2, $value = "three"????
So I cannot understand what's going on. Our temporal solution is to iterate over the original object and unset from the copy. Does anybody knows which change in the PHP core (or ArrayObject/ArrayIterator) makes this? I have search about it but some people has this problem with foreach were the item iterated is by reference.
If you switch between PHP 5.6 and 7, the behaviour changes.
Example 1 (with iterator)
$elements = new ArrayObject();
$elements->append('one');
$elements->append('two');
$elements->append('three');
$elements->append('four');
print_r($elements);
$clone = clone $elements;
$it = $clone->getIterator();
echo "\n------\n";
foreach ($it as $key => $value) {
echo $key."\t=>\t".$value."\n";
if ($value == 'two' || $value == 'three') {
$it->offsetUnset($key);
}
}
echo "\n------\n";
print_r($clone);
Example 2 (without iterator)
$elements = new ArrayObject();
$elements->append('one');
$elements->append('two');
$elements->append('three');
$elements->append('four');
print_r($elements);
$clone = clone $elements;
echo "\n------\n";
foreach ($clone as $key => $value) {
echo $key."\t=>\t".$value."\n";
if ($value == 'two' || $value == 'three') {
$clone->offsetUnset($key);
}
}
echo "\n------\n";
print_r($clone);
Thanks so much!
From my understanding , it is considered a bad practice to
modify an array while looping through it, and the proper way to do it would be using array_filter.
Since you have an ArrayObject, one solution would be to export it to an array, filter it using array_filter and create a new ArrayObject from the filtered array.
See also here : Filter ArrayObject (PHP)
Probably this behavior is due to the fact that loops are handled differently in php7. As mentioned here: http://php.net/manual/en/control-structures.foreach.php in php5 foreach uses an internal array pointer in contrast to php7.
I'm trying to foreach through an array of objects inside an array. I'm not receiving errors, but it seems not to be storing the new value I'm trying to establish. I've found many answers on here closely related to this question, but I'm not understanding, and I'm hoping someone can make this clearer.
Essentially, this converts kg to lbs.
$kg_conv = 2.20462262;
$weights = array($data['progress']->weight, $data['progress']->squats,
$data['progress']->bench, $data['progress']->deadlift);
foreach ($weights as $value) {
$value = $value * $kg_conv;
}
The values in the array come out unchanged. From what I've read, I should be using $data['progress'] and iterating through that, but I'm not understanding how to refer to only some of the elements inside instead of all of them. I also tried cutting out some redundancy by storing the objects in $progress instead of $data['progress'] but still not success.
UPDATE: The following has not worked
1
foreach ($weights as &$value) {
$value = $value * $kg_conv;
}
2
foreach ($weights as $key => &$value) {
$value = $value * $kg_conv;
}
3
foreach ($weights as $key => $value) {
$weights[$key] = $value * $kg_conv;
}
it does not work because you do not modify data array, you only modify copy made from this array, which is another array in memory
Solution: (will work for object's fields too)
$kg_conv = 2.20462262;
$weights = ['weight', 'squats','bench', 'deadlift'];
foreach ($data['progress'] as $key=>&$value) {
if ( in_array($key,$weights)
$value = $value * $kg_conv;
}
$value contains only a copy of the array element.
You can pass the object by reference like this:
foreach ($weights as &$value) {
$value = $value * $kg_conv;
}
Note the & before $value.
See References Explained in the php documentation or What's the difference between passing by reference vs. passing by value? here on stackoverflow.
Edit 2:
But note that this will only change the values in the $weights array, not your object stored in $data['progress'], as it's value has been copied into $weights. To achieve this, you can reference the object properties when constructing the data array in the following way (this step was commented out in my testcase code) :
$weights = array(&$data['progress']->weight, &$data['progress']->squats,
&$data['progress']->bench, &$data['progress']->deadlift);
Note the & operator again.
But probably the solution of maxpower is cleaner for your requirement.
Edit:
After your comment I created the following test case which works fine:
<?php
class foo {
public $test = 5;
public $test2 = 10;
}
$obj = new foo();
$obj2 = new foo();
$obj2->test = 3;
$data = array('progress' => $obj, 'p2' => $obj2);
// this copies the values into the new array
$weights = array($data['progress']->test, $data['progress']->test2);
// this makes a reference to the values, the original object will be modified
// $weights = array(&$data['progress']->test, &$data['progress']->test2);
var_dump($weights);
$kg_conv = 2.20462262;
foreach ($weights as &$value) {
$value = $value * $kg_conv;
}
var_dump($weights);
The result is:
array(2) {
[0]=>
int(5)
[1]=>
int(10)
}
array(2) {
[0]=>
float(11.0231131)
[1]=>
&float(22.0462262)
}
The PHP foreach manual describes this topic in great detail. To summarise why you are not seeing the results you expect, here is a short explanation.
Imagine you have an array $arr = ['one', 'two', 'three']. When you execute a foreach loop on this array, PHP will internally create a copy of your array:
foreach ($arr as $key => $value) {
$value = 'something'; // You are modifying the copy, not the original!
}
To modify the original array, you have two options.
Use the $key variable to access the element at given key in the original array:
$arr[$key] = 'something'; // Will change the original array
Tell PHP to use a reference to the original element in $value:
foreach ($arr as $key => &$value) {
$value = 'something'; // $value is reference, it WILL change items in $arr
}
Both approaches are valid; however, the second is more memory-efficient because PHP does not have to copy your array when you loop through it - it simply references the original.
In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference. In your example code should look like this:
foreach ($weights as &$value) {
$value = $value * $kg_conv;
}
But referencing $value is only possible if the iterated array can be referenced (i.e. if it is a variable).
You can also use indexes to acces array value directly. This code works pretty fine too:
foreach ($weights as $key => $value) {
$weights[$key] = $value * $kg_conv;
}
More here: http://php.net/manual/en/control-structures.foreach.php
$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.
I want to loop through an array with foreach to check if a value exists. If the value does exist, I want to delete the element which contains it.
I have the following code:
foreach($display_related_tags as $tag_name) {
if($tag_name == $found_tag['name']) {
// Delete element
}
}
I don't know how to delete the element once the value is found. How do I delete it?
I have to use foreach for this problem. There are probably alternatives to foreach, and you are welcome to share them.
If you also get the key, you can delete that item like this:
foreach ($display_related_tags as $key => $tag_name) {
if($tag_name == $found_tag['name']) {
unset($display_related_tags[$key]);
}
}
A better solution is to use the array_filter function:
$display_related_tags =
array_filter($display_related_tags, function($e) use($found_tag){
return $e != $found_tag['name'];
});
As the php documentation reads:
As foreach relies on the internal array pointer in PHP 5, changing it within the loop may lead to unexpected behavior.
In PHP 7, foreach does not use the internal array pointer.
foreach($display_related_tags as $key => $tag_name)
{
if($tag_name == $found_tag['name'])
unset($display_related_tags[$key];
}
Instead of doing foreach() loop on the array, it would be faster to use array_search() to find the proper key. On small arrays, I would go with foreach for better readibility, but for bigger arrays, or often executed code, this should be a bit more optimal:
$result=array_search($unwantedValue,$array,true);
if($result !== false) {
unset($array[$result]);
}
The strict comparsion operator !== is needed, because array_search() can return 0 as the index of the $unwantedValue.
Also, the above example will remove just the first value $unwantedValue, if the $unwantedValue can occur more then once in the $array, You should use array_keys(), to find all of them:
$result=array_keys($array,$unwantedValue,true)
foreach($result as $key) {
unset($array[$key]);
}
Check http://php.net/manual/en/function.array-search.php for more information.
if you have scenario in which you have to remove more then one values from the foreach array in this case you have to pass value by reference in for each:
I try to explain this scenario:
foreach ($manSkuQty as $man_sku => &$man_qty) {
foreach ($manufacturerSkus as $key1 => $val1) {
// some processing here and unset first loops entries
// here dont include again for next iterations
if(some condition)
unset($manSkuQty[$key1]);
}
}
}
in second loop you want to unset first loops entries dont come again in the iteration for performance purpose or else then unset from memory as well because in memory they present and will come in iterations.
There are already answers which are giving light on how to unset. Rather than repeating code in all your classes make function like below and use it in code whenever required. In business logic, sometimes you don't want to expose some properties. Please see below one liner call to remove
public static function removeKeysFromAssociativeArray($associativeArray, $keysToUnset)
{
if (empty($associativeArray) || empty($keysToUnset))
return array();
foreach ($associativeArray as $key => $arr) {
if (!is_array($arr)) {
continue;
}
foreach ($keysToUnset as $keyToUnset) {
if (array_key_exists($keyToUnset, $arr)) {
unset($arr[$keyToUnset]);
}
}
$associativeArray[$key] = $arr;
}
return $associativeArray;
}
Call like:
removeKeysFromAssociativeArray($arrValues, $keysToRemove);