Foreach and unset() - php

Ran into a little snag and wondering if there is a "best practices" way around it.
So I just learned that "A php foreach will execute on the entire array regardless. Test unsetting a value that is next in iteration. It will iterate on the offset, but the value will be null. – Kevin Peno Dec 22 '09 at 21:31" from How do you remove an array element in a foreach loop?
It's the first part of that that is messing with me. I'm iterating through an array with foreach. It's a search function so I'm removing the element I just searched for, so when the loop runs again its minus that element.
I do NOT want to reindex if at all possible, although if I have to I can.
Array
(
[0] => Array
(
[0] => a
[1] => aa
[2] => aaa
)
[1] => Array
(
[0] => b
[1] => bb
[2] => bbb
)
[2] => Array
(
[0] => c
[1] => cc
[2] => ccc
)
[3] => Array
(
[0] => d
[1] => dd
[2] => ddd
)
)
foreach($array as $key=>$value) {
$searchresult[] = search function returns various other keys from array
foreach($searchresult as $deletionid) {
unset($array[$deletionid]);
}
}
So on the first iteration it uses $array[0] obviously but the $searchresults might return 4,5,6,7. So those keys are removed from $array.
Yet the foreach loop still iterates through those and gives me back a bunch of empty arrays.
I did read How does PHP 'foreach' actually work? and I get some of it.
Thanks

In my opinion, the best way to remove array elements based on indexes is to use the array_* set of functions, like array_diff and array_intersect (or array_diff_key and array_intersect_key in your situation).
$indexes_to_remove = array(2,3,4);
$indexes_to_remove = array_flip($indexes_to_remove);
$array = array_diff_key($array,$indexes_to_remove);

If the array is guaranteed to be exhausted at some point, you can use this:
while (true) {
$searchresult[] = search function returns various other keys from array
foreach($searchresult as $deletionid) {
unset($array[$deletionid]);
}
if (count($array) === 0) {
break;
}
}
And yes I know while (true) is pretty evil, but I find in cases like these it does exactly what is needed.
If you want to prevent it from infinite looping you could always add a variable, increment each iteration, and break when it reaches a high value that should never happen (like 10 * count($array))

Related

Find the maximum and parent value in an array

I searched many thread but i can't find this solution
I have this Array
Array
( [0] => [1] => Array ( [0] => 2019-01-11T23:30:00CET [1] => -12.6 ) [2] => [3] => Array ( [0] => 2019-01-11T23:20:00CET [1] => -12.5 ) [4] => [5] => Array ( [0] => 2019-01-11T23:10:00CET [1] => -12.6 ) [10] => [11] => Array ( [0] => 2019-01-11T22:40:00CET [1] => -12.4 )
I found the path to have the maximum or minimum value ( Column [1] ) from this Array but i need to find the relative Parent
(example the minimum -12.6 is in the [1][0] as 2019-01-11T22:20:00CET)
of this two values that are show in the first column ( Column[0] )
Thanks
If you use array_column() to extract the second column of your data, then you can use min() or max() with that array to pick which one you want. This code then extracts the ones that match using a foreach() and if to check if it matches (not exactly sure what you want as output, but this should help)...
$source = [["2019-01-11T23:30:00CET", -12.6],
["2019-01-11T23:20:00CET", -12.5],
["2019-01-11T23:10:00CET", -12.6]
];
$extract = min(array_column($source, 1)); // or use max()
$output = [];
foreach ($source as $key => $element) {
if ( $element[1] == $extract ) {
// Matches, so add to output
$output[$key] = $element[0];
}
}
print_r($output);
will give
Array
(
[0] => 2019-01-11T23:30:00CET
[2] => 2019-01-11T23:10:00CET
)
You could use array_filter() to extract the matching rows, but a foreach() is enough for a straightforward thing like this (IMHO).
If there is a possibility of blank values or strings in the value column, this may confuse the min() as it will consider the values and compare them as strings, to ensure they are all compared as numbers you can add...
$values = array_map("floatval", array_column($source, 1));
$extract = min($values); // or use max()
The array_map("floatval",... goes through the list and converts them all to float values.
Also, here's a generalized algorithm-sketch for "finding the max in some array", expressed as pseudo-code:
"Leave quietly" if the array is empty, or throw an exception.
Otherwise, assume that the first element in the array is the biggest one.
Now, loop through the remaining elements, testing if each one is, in fact, bigger than the "biggest one" that you have so far. If so, select it as the "biggest."
When the loop is finished, return your answer.
Now – this is what a geek would call "an O(n) algorithm," which is to say that its execution-time will be "on the order of" the number of elements in the array. Well, if this is a "one-off" requirement, that's fine. Whereas if what you actually want to do is to get "more than one" max-element, sorting the array (once, then holding on to the sorted result ...) becomes significantly better, because the sort is going to be O(log(n)) ... "on the order of some logarithm of the number of elements," ... and the subsequent cost of "popping off" elements from that sorted array becomes non-existent.
There are other ways to do it, of course – trees and such - but I've already blathered-on too long here.

2 or more arrays from a list

I am quite not able to get the logic for my requirement.
Lets consider I have an array
Array
(
[0] => 1
[1] => 1
[2] => 2
[3] => 1
[4] => 3
[5] => 3
)
I would like to split the below array on the basis if the array element matches its previous element.
i.e in the above example value at [1] matches the value at [0]. Hence put it into the same array as [0]. Now check if the value at [2] matches the value at [1] if it matches put it into the same array, if not put it into a different array. The process continues.
Below is an example of the desired outpout.
Array
(
[0] => 1
[1] => 1
)
Array
(
[0] => 2
)
Array
(
[0] => 1
)
Array
(
[0] => 3
[1] => 3
)
Thanks for your help in advance.
Justin
you can obtain that result in a loop checking on previous element. the output can be an array of arrays! (or anything you would prefer.. do your thing here)
$array1 = array(1, 1, 2, 1, 3, 3);
$output_array=array();
$previous_value="";
$output_array_index=0;
foreach ($array1 as $value) {
if($value != $previous_value){
$output_array_index+=1;
}
$output_array[$output_array_index][]=$value;
$previous_value=$value;
}
print_r($output_array);
so, let me know if you need more pointers! array logic is fun, and php will let you do alot, out of the box. though this specific need is not covered, have a look when you have a minute # the manual, it'll save you time in the future, guarantee http://php.net/manual/en/ref.array.php
This question is a bit confusing but doesn't sound too difficult to implement if I'm understanding it correctly. All you need to do is have a temporary array (or array list) that checks user input. If that user input happens to be the same as the previous input (you can keep a counter variable and check to see if ArrayList.get(counter) == ArrayList.get(counter-1)). Keep adding things to this temporary arrayList and once you have a number that is different, just iterate through the arraylist and add it to a new array.
Another question you have to consider is how you are going to store all these arrays. For that you may want to create an ArrayList containing Arrays. That way after you find user input that is different from the previous input you can just use the toArray method provided with the ArrayList class and add it to the ArrayList containing all of your separate Arrays!
Hope this helps!

Comparing arrays until I have a unique array

I have the following multidimensional array called $existing_combinations
Array
(
[0] => Array
(
[1] => 6
[2] => 7
[3] => 9
)
[1] => Array
(
[1] => 1
[2] => 21
[3] => 9
)
[2] => Array
(
[1] => 1
[2] => 7
[3] => 9
)
)
I then generate a new array ($new_combination) which has a combination of the same set of values. Example:
Array
(
[1] => 6
[2] => 21
[3] => 9
)
I then test if $new_combination exists in $existing_combinations with the following in the hope that I will end with a unique combination in $new_combination:
foreach($existing_combinations as $key => $combination){
while($new_combination == $combination){
$new_combination = generateNewCombination();
}
}
The problem is that if $new_combination matches an array that is not at index 0, then when I generate a new combination I am at risk of this matching a $combination that has already been tested against (and will not be tested again).
Sorry if this is a simple one but I'm struggling to think of how I can ensure $new_combination will always end up unique.
Any ideas?
Thanks
You can use in_array in this case, because php compares arrays as value. So, the code can be:
while(in_array($new_combination = generateNewCombination(), $existing_combinations));
print_r($new_combination);
I wrote the below before realizing that in_array can also see if an array exists within an array. So you can simply do this:
if (!in_array($new_combination, $existing_combinations)) {
// It's unique.
}
In the below outdated bit, see the note on using sort, if a different sequence of the same numbers isn't considered unique for your purposes.
[ For Entertainment ]
May not be the most elegant way around, but I would simply do this to keep it simple:
$combo_hashes = [];
// Implode the existing combos into strings.
foreach($existing as $vals) {
$combo_hashes[] = implode(':', $vals);
}
Then, all you need to check with your new combo is:
// Check if the "hash"-string already exists.
if (!in_array( implode(':', $new_combo), $combo_hashes)) {
// ... and we're unique.
}
This presumes that you consider [1,3,2] different from [2,1,3]. If they are equivalent (I don't know what your use case is), you should sort($vals); before you generate the check-strings.
Merge all the second level arrays and run array_unique() to get rid of the duplicate values.

Two PHP arrays, unset positions that are only present in one array

I have two arrays (in PHP):
ArrayA
(
[0] => 9
[1] => 1
[2] => 2
[3] => 7
)
ArrayB
(
[0] => 1
[1] => 1
[3] => 8
)
I want to make two new arrays, where I have only the elements declared in both of the arrays, like the following:
ArrayA
(
[0] => 9
[1] => 1
[3] => 7
)
ArrayB
(
[0] => 1
[1] => 1
[3] => 8
)
In this example ArrayA[2] doesn't exist, so ArrayB[2] has been unset.
I wrote this for loop:
for ($i = 0, $i = 99999, $i++){
if (isset($ArrayA[$i]) AND isset($ArrayB[$i]) == FALSE)
{
unset($ArrayA[$i],$ArrayB[$i]);
}
}
But it's not great because it tries every index between 0 and a very big number (99999 in this case). How can I improve my code?
The function you're looking for is array_intersect_key:
array_intersect_key() returns an array containing all the entries of array1 which have keys that are present in all the arguments.
Since you want both arrays, you'll have to run it twice, with the parameters in opposite orders, as it only keeps keys from the first array. An example:
$arrayA_filtered = array_intersect_key($arrayA, $arrayB);
$arrayB_filtered = array_intersect_key($arrayB, $arrayA);
Also, although a for loop wasn't ideal in this case, in other cases where you find yourself needing to loop through sparse array (one where not every number is set), you can use a foreach loop:
foreach($array as $key => $value) {
//Do stuff
}
One very important thing to note about PHP arrays is that they are associative. You can't simply use a for loop, as the indices are not necessarily a range of integers. Consider what would happen if you applied this algorithm twice! You'd get out of bounds errors as $arrayA[2] and $arrayB[2] no longer exist!
I would iterate through the arrays using nested foreach statements. I.e.
$outputA = array();
$outputB = array();
foreach ($arrayA as $keyA => $itemA) {
foreach ($arrayB as $keyB => $itemB) {
if ($keyA == $keyB) {
$outputA[$keyA] = $itemA;
$outputB[$keyB] = $itemB;
}
}
This should give you two arrays, $outputA and $outputB, which look just like $arrayA and $arrayB, except they only include key=>value pairs if the key was present in both original arrays.
foreach($arrayA as $k=>$a)
if (!isset($arrayB[$k]))
unset($arrayA[$k];
Take a look to php : array_diff
http://docs.php.net/manual/fr/function.array-diff.php

I honestly have no clue how to formulate this pass-by-reference conundrum

So here's what I see this code doing:
An array is made
A loop iterates 10 times
A new array is created
A reference to this new array is saved in the first array
10 arrays now reside in the original array with values 0, 1, 2, 3...
What really happens:
WTF?
Code:
<?php
header('Content-type: text/plain');
$arrays = array();
foreach(range(0, 10) as $i)
{
$arr = array();
$arr[0] = $i;
$arrays[] = &$arr;
}
print_r($arrays);
Output:
Array
(
[0] => Array
(
[0] => 10
)
[1] => Array
(
[0] => 10
)
[2] => Array
(
[0] => 10
)
[3] => Array
(
[0] => 10
)
[4] => Array
(
[0] => 10
)
[5] => Array
(
[0] => 10
)
[6] => Array
(
[0] => 10
)
[7] => Array
(
[0] => 10
)
[8] => Array
(
[0] => 10
)
[9] => Array
(
[0] => 10
)
[10] => Array
(
[0] => 10
)
)
I would like to know exactly why apparently only the 10th array is referred to ten times, instead of every instance of the arrays being referred to one each.
Also if somebody who isn't just thinking WTF (like me) would like to edit the title, feel free to do so.
The line
$arr = array();
does not create a new array but rather assigns an empty array to the already existing reference.
If you want the variable name to "point" to a different array in memory, you have to unset() (or "disconnect") it first before assigning an empty array to it:
foreach(range(0, 10) as $i)
{
unset($arr);
$arr = array();
$arr[0] = $i;
$arrays[] = &$arr;
}
This is because the only operations that can make a variable point to something else is the reference assignment (=&) and the unset().
What happens here is that by inserting a reference to $arr inside $arrays, you are effectively adding the exact same array 10 times -- and each reference to the array has the value last assigned to it (i.e. the one produced when $i is 10).
It's not clear what you intend to achieve by inserting a reference in each iteration -- either removing the & or putting unset($arr) at the beginning of the loop would give you the expected behavior. What are you trying to accomplish?
Think about it this way. You do $arrays[] = &$arr; 10 times. This stores a reference to the local variable $arr 10 times. Since it's the same variable (the variable's scope is the entire function), it stores the same reference all 10 times. Thus, why should you expect the 10 elements to be different?
The reference you are storing has nothing to do with the value of $arr; it just has to do with the variable $arr. When you print the reference it prints the value of $arr at that time.
It's because you're storing a reference to the array that $arr points to in the array. And you keep overwriting that array with the latest number. All references in $arr will point to the same array in the end.
I don't know what you expect to get out of this in the end, but getting rid of & should fix this behavior.

Categories