I want to unset every second item from an array. I don't care about if the keys are reordered or not.
Of course I want it fast and elegant. Is it maybe possible without a loop and temporary variables?
My own solution so far:
for ( $i = 1; isset($arr[$i]); $i += 2) {
unset($arr[$i]);
}
The pro is, that it needs no if-statement, the con that a variable ($i) is still needed and it works only if the keys are numeric and without gaps.
function arr_unset_sec(&$arr, $key)
{
if($key%2 == 0)
{
unset($arr[$key]);
}
}
array_walk($arr, 'arr_unset_sec');
Assuming $arr may be some array. Check this piece of code.
If you have an array like
Array
(
[0] => test1
[1] => test2
[2] => test3
[3] => test4
[4] => test5
)
Then you can go with below code. It will remove every second item of array.
$i = 1;
foreach ($demo_array as $key => $row) {
if($i%2 == '0')
{
unset($demo_array[$key]);
}
$i++;
}
Hope this will helps you. Let mee know if you need any further help on it.
Another solution without a loop:
$arr = array('a', 'b', 'c', 'd', 'e');
$arr = array_filter( $arr, function($k) { return $k % 3 === 0; }, ARRAY_FILTER_USE_KEY);
Pro, it needs no loop. Cons, it is a lot slower than my other version (with a for loop), looks a bit scary and depends again on the keys.
I'll provide two methods (array_filter() and a foreach() loop) which will leverage the condition $i++%$n to target the elements to be removed.
Both methods will work on indexed and associative arrays.
$i++ This is post-incrementation. Effectively, the value will be evaluated first, then incremented second.
% This is the modulo operator - it returns the "remainder" from the division of the leftside value from the rightside value.
The return value from the condition will either be 0 or a positive integer. For this reason, php's inherent "type juggling" feature can be used to convert 0 to false and positive integers as true.
In the array_filter() method, the use() syntax must use &$i so that the variable is "modifiable". Without the &, $i will remain static (unaffected by post-incrementation).
In the foreach() method, The condition is inverted !() in comparison to the array_filter() method. array_filter() wants to know what to "keep"; foreach() wants to know what to unset().
Code: (Demo)
// if:$n=2 $n=3 $n=4 $n=5
$array=['first'=>1,
2, // remove
'third'=>3, // remove
'fourth'=>4, // remove remove
5, // remove
6, // remove remove
'seventh'=>7,
'eighth'=>8, // remove remove
'ninth'=>9]; // remove
// if $n is 0 then don't call anything, because you aren't attempting to remove anything
// if $n is 1 then you are attempting to remove every element, just re-declare as $array=[]
for($n=2; $n<5; ++$n){
$i=1; // set counter
echo "Results when filtering every $n elements: ";
var_export(array_filter($array,function()use($n,&$i){return $i++%$n;}));
echo "\n---\n";
}
echo "\n\n";
// Using a foreach loop will be technically faster (only by a small margin) but less intuitive compared to
// the literal/immediate interpretation of "array_filter".
for($n=2; $n<5; ++$n){
$i=1;
$copy=$array;
foreach($copy as $k=>$v){
if(!($i++%$n)) unset($copy[$k]); // or $i++%$n==0 or $i++%$n<1
}
echo "Results when unsetting every $n elements: ";
var_export($copy);
echo "\n---\n";
}
Output:
Results when filtering every 2 elements: array (
'first' => 1,
'third' => 3,
1 => 5,
'seventh' => 7,
'ninth' => 9,
)
---
Results when filtering every 3 elements: array (
'first' => 1,
0 => 2,
'fourth' => 4,
1 => 5,
'seventh' => 7,
'eighth' => 8,
)
---
Results when filtering every 4 elements: array (
'first' => 1,
0 => 2,
'third' => 3,
1 => 5,
2 => 6,
'seventh' => 7,
'ninth' => 9,
)
---
Results when unsetting every 2 elements: array (
'first' => 1,
'third' => 3,
1 => 5,
'seventh' => 7,
'ninth' => 9,
)
---
Results when unsetting every 3 elements: array (
'first' => 1,
0 => 2,
'fourth' => 4,
1 => 5,
'seventh' => 7,
'eighth' => 8,
)
---
Results when unsetting every 4 elements: array (
'first' => 1,
0 => 2,
'third' => 3,
1 => 5,
2 => 6,
'seventh' => 7,
'ninth' => 9,
)
---
$n = 1
for( $i=$n;$i=$n;)
{
unset($arOne[$i]);
unset($arSnd[$i]);
unset($arThd[$i]);
break;
}
I think this will also perfectly.
Related
I have a four-level multidimensional array. I need to sort in ascending order (ASC) the numeric "leaves" in order to calculate the median of the values.
I tried array_walk_recursive(), array_multisort(), usort(), etc. but was unable to find a working solution.
Here's a schematic of the array:
(
[2017-05-01] => Array
(
[DC] => Array
(
[IT] => Array
(
[0] => 90
[1] => 0
)
[DE] => Array
(
[0] => 18
[1] => 315
[2] => 40
[3] =>
[4] => 69
)
[Other] => Array
(
[0] => 107
[1] => 46
[2] =>
[3] =>
[4] => 27
[5] => 22
)
)
)
)
This will output the deepest subarrays' median values using the input array's structure.
I'm including hard-casting of median values (one or both in a subset) as integers in the event that the value(s) are empty strings. I'll also assume that you will want 0 as the output if a subset is empty.
Code: (Demo)
$array=[
'2017-05-01'=>[
'DC'=>[
'IT'=>[90, 0],
'DE'=>[18, 315, 40, '', 69, 211],
'Other'=>[107, 46, '', '', 27, 22]
]
],
'2017-05-02'=>[
'DC'=>[
'IT'=>[70, 40, 55],
'DE'=>['', 31, 4, '', 9],
'Other'=>[1107, 12, 0, 20, 1, 11, 21]
]
],
'fringe case'=>[
'DC'=>[
'IT'=>[],
'DE'=>['', '', '', 99],
'Other'=>['', 99]
]
]
];
foreach ($array as $k1 => $lv1) {
foreach ($lv1 as $k2 => $lv2) {
foreach ($lv2 as $k3 => $lv3) {
sort($lv3); // order values ASC
$count = sizeof($lv3); // count number of values
$index = floor($count / 2); // get middle index or upper of middle two
if (!$count) { // count is zero
$medians[$k1][$k2][$k3] = 0;
} elseif ($count & 1) { // count is odd
$medians[$k1][$k2][$k3] = (int)$lv3[$index]; // single median
} else { // count is even
$medians[$k1][$k2][$k3] = ((int)$lv3[$index-1] + (int)$lv3[$index]) / 2; // dual median
}
}
}
}
var_export($medians);
Output:
array (
'2017-05-01' =>
array (
'DC' =>
array (
'IT' => 45,
'DE' => 54.5,
'Other' => 24.5,
),
),
'2017-05-02' =>
array (
'DC' =>
array (
'IT' => 55,
'DE' => 4,
'Other' => 12,
),
),
'fringe case' =>
array (
'DC' =>
array (
'IT' => 0,
'DE' => 0,
'Other' => 49.5,
),
),
)
*for the record, $count & 1 is a bitwise comparison that determines if the value is odd without performing arithmetic (and is the most efficient way of performing this check within php).
*also, if you wanted to simply overwrite the values of the input array, you could modify by reference by writing & before $lv1, $lv2, and $lv3 in the foreach declarations then save the median value to $lv3. Demo The benefit in doing so removes key declarations and making your code more brief.
As it turns out, there is a way to do what the OP seeks using a combination of usort() and array_walk(), each of which takes a callback, as follows:
<?php
// median code:
//http://www.mdj.us/web-development/php-programming/calculating-the-median-average-values-of-an-array-with-php/
function calculate_median($arr) {
sort($arr);
$count = count($arr); //total numbers in array
$middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value
if($count % 2) { // odd number, middle is the median
$median = $arr[$middleval];
} else { // even number, calculate avg of 2 medians
$low = $arr[$middleval];
$high = $arr[$middleval+1];
$median = (($low+$high)/2);
}
return $median;
}
$a = [];
$a["2017-05-01"] = ["DC"];
$a["2017-05-01"]["DC"]["IT"] = [90,0];
$a["2017-05-01"]["DC"]["DE"] = [18,315,40,"",69];
$a["2017-05-01"]["DC"]["Other"] = [107,46,"","",27,22];
function sort_by_order ($a, $b)
{
if ($a == "") $a = 0;
if ($b == "") $b = 0;
return $a - $b;
}
function test($item,$key){
echo $key," ";
if (is_array($item)) {
echo array_keys($item)[1],"\n";
$popped = array_pop($item);
foreach ($popped as $key => $arr) {
usort($arr, 'sort_by_order');
echo "Median ($key): ",calculate_median( $arr ),"\n";
}
}
}
array_walk($a, 'test');
See demo here. Also, see this example based on the OP's sandbox.
Although the OP's code does not show the array keys as quoted, beware they should be in the actual code, otherwise PHP will do math with 2017-05-01 and you'll see a key of 2011. Interesting read here about usort.
The median code I extracted from here.
Interestingly, the conventional wisdom about sorting numbers to determine the median is not necessarily the only way to obtain that result. Apparently, it can also be done and perhaps more efficiently by finding a pivot number and dividing the series of numbers into three parts (see this response).
I'm probably [super]overthinking this. I'm trying to analyze an array with values like [1,9], [4,6] [5,5], [6,4], [9,1] and duplicate digits (I'm having a super brain fart and can't even remember the term for numbers like this) remove (the last two) so that only [1,9], [4,6] [5,5] are printed.
I was thinking that turning this array into a string and using preg_match, but I'm pretty sure this wouldn't work even if I had the correct regex.
If you have an array of pairs like this:
$x = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1)
);
Here is one way to get the unique pairs:
foreach ($x as $pair) {
sort($pair);
$unique_pairs[implode(',', $pair)] = $pair;
}
This uses string representations of each sorted pair as keys in a new array, so the result will have distinct values by definition.
As far as the printing them out part of your question, once you have the unique values you can loop over them and print them out in whichever format you like, for example:
foreach ($unique_pairs as $pair) { vprintf("[%d,%d]<br>", $pair); }
It looks like elements are distributed symmetrically.
We can cut the array in two halves and get only the first half with array_slice():
$array = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 4
[1] => 6
)
[2] => Array(
[0] => 5
[1] => 5
)
)
Demo at Codepad.
ceil() is used to round the number up to the next highest integer if there is an even number of items in the array. Example: if there is 3 items in the array, 5 / 2 will return 2.5, we want 3 items so we use ceil(2.5) which gives 3.
Example with 3 items:
$array = array(
array(1,9),
array(5,5),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 5
[1] => 5
)
)
Example with 4 items:
$array = array(
array(1,9),
array(7,7),
array(7,7),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 7
[1] => 7
)
)
If I'm correct in understanding what you are trying to do, you want to remove the final 2 elements from the array?
There is a function in PHP called array_pop that removes the final element from the array.
$array = array_pop($array);
So if you run this twice, you will remove the final 2 elements from the array.
This is how I'd do it (and I hope I am not overthinking this :))
$stringArray = array();
$stringArray[] = '1,9';
$stringArray[] = '4,6';
$stringArray[] = '5,5';
$stringArray[] = '6,4';
$stringArray[] = '9,1';
foreach($stringArray as &$numString) {
$numString = explode(',', $numString);
usort($numString, function($a, $b) {return $a - $b;});
$numString = implode(',', $numString);
}
$a = array_unique($a);
print_r($a);
You basically explode every element into a subarray, sort it and then implode it back. After calling the array_unique, you're left with unique values in the array.
The output would be
Array
(
[0] => 1,9
[1] => 4,6
[2] => 5,5
)
The result you suggest treats [a,b] as equivalent to [b,a] which makes the problem a lot more complex. The code below gives the result you asked for, but without really understanding what the problem is that you are trying to fix and whether [1,9] is equivalent to [9,1] in the solution:
$a=array(array(1,9),array(4,6),...
$dup=array();
for ($i=0; $i<count($a) -1; $i++) {
for ($j=$i+1; $j<count($a); $j++) {
if (($a[$i][0]==$a[$j[0] && $a[$i][1]==$a[$j[1])
|| ($a[$i][0]==$a[$j[1] && $a[$i][1]==$a[$j[0])) {
$dup[]=$j;
}
}
}
foreach ($dup as $i) {
unset($a[$i]);
}
So I'm actually going to assume your question to have a different meaning than everyone else did. I believe what you're asking is:
How do you filter out array items where a reverse of the item has already been used?
<?php
// The example set you gave
$numberSets = [[1, 9], [4, 6], [5, 5], [6, 4], [9, 1]];
// Initialize an empty array to keep track of what we've seen
$keys = [];
// We use array filter to get rid of items we don't want
// (Notice that we use & on $keys, so that we can update the variable in the global scope)
$numberSets = array_filter($numberSets, function($set) use(&$keys) {
// Reverse the array
$set = array_reverse($set);
// Create a string of the items
$key = implode('', $set);
// Get the reverse of the numbers
$reversedKey = strrev($key);
// If the palindrome of our string was used, return false to filter
if (isset($keys[$reversedKey])) {
return false;
}
// Set the key so it's not used again
// Since $keys is being passed by reference it is updated in global scope
$keys[$key] = true;
// Return true to NOT filter this item, since it or it's reverse were not matched
return true;
});
var_dump($numberSets);
This is my code:
$a = array(
array("a" => 1, "b" => 2),
array("x" => 2, "a" => 2),
array("d" => 100, "a" => 3, "b" => 2, "c" => 3)
);
$myArray = array();
foreach ($a as $arr) {
$myArray[] = $arr['a'];
}
print_r($myArray);
So, I get
Array
(
[0] => 1
[1] => 2
[2] => 3
)
Is there any other way to do this without for loop? like using one are two PHP array functions to get the same response.
The above is correct but still if there is any other better way to do this that would be appreciable! Because the same array $a in my code is required to be iterate many times. If I have any better way to do this so I can reduce another iteration( PHP still does iteration in built-in fns, I don't bother it).
Yes (since you're on PHP 5.4, array_column() isn't an option),
$result = array_map(function($x)
{
return $x['a'];
}, $a);
But note, this will still use loop internally (i.e. in the end it always be a loop)
I'm kind of stuck here on what I think must have quite a simple solution.
Say I have an array:
$A = array(1, 1, 2, 4, 6, 7, 7, 7, 13);
How could I possibly remove all of the values that occur more than once?
So I'm left with an array that looks like this
$A = array(2, 4, 6, 13);
I've tried using array unique, but that just removes duplicates leaving you with a single value. I need to use the following logic: if there are any values that match - then remove all of the values that match.
You could always try something like this.
$A = array(1, 1, 2, 4, 6, 7, 7, 7, 13);
$A = array_count_values($A);
foreach($A as $key => $value) {
if($value > 1)
unset($A[$key]);
}
$A = array_keys($A);
print_r($A);
edit: fixed error
Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 13
)
You can use array_filter() with a custom callback to filter out the array values which repeat more than once:
function removeDuplicates($array) {
$values = array_count_values($array);
return array_filter($array, function($item) use ($values) {
return $values[$item] === 1;
});
}
Usage:
$A = array(1, 1, 2, 4, 6, 7, 7, 7, 13);
print_r( removeDuplicates($A) );
Output:
Array
(
[2] => 2
[3] => 4
[4] => 6
[8] => 13
)
Demo.
You can do this with abit of coding.
First see this SO post to get a list of all the duplicates using array_unique:
php return only duplicated entries from an array
Then you'll have to loop through the duplicates returned from the link above and do an array_search to return the index of the values and then array_splice to actually remove it.
I don't know of any code that will do this in one step for you.
Try this (with PHP >= 5.3):
$A = array_keys(array_filter(
array_count_values($A),
function ($count)
{
return $count == 1;
}
));
Explanation:
array_count_values returns an array using the values of array as keys and their frequency in array as values.
array_filter Iterates over each value in the array passing them to the callback function (in this case anonymous function) . If the frequency is 1, the current value from array is returned into the result array.
array_keys returns the keys from the array, in this case the values with frequency equal to 1.
So, the compressed "one-line" form is:
$A=array_keys(array_filter(array_count_values($A),function($c){return $c==1;}));
I need to reduce the size of this array to X, so i would like to remove X random items. Here's my PHP array:
Array
(
[helping] => 3
[me] => 2
[stay] => 1
[organized!] => 1
[votre] => 4
[passion,] => 1
[action,] => 1
[et] => 2
[impact!] => 1
[being] => 4
)
I tried array_rand() but it didn't keep the keys/values.
array_rand() returns a random key (or more) of the given array, unset using that:
$randomKey = array_rand($array, 1);
unset($array[$randomKey]);
array_rand() returns an array with keys from your original array.
You would need to delete the keys from your original array using a foreach-loop.
Like this:
// Suppose you need to delete 4 items.
$keys = array_rand($array, 4);
// Loop through the generated keys
foreach ($keys as $key) {
unset($array[$key]);
}
$foo = Array(
"helping" => 3,
"me" => 2,
"stay" => 1,
"organized!" => 1,
"votre" => 4,
"passion," => 1,
"action," => 1,
"et" => 2,
"impact!" => 1,
"being" => 4
);
$max = 5; //number of elements you wish to remain
for($i=0;$i<$max;$i++){ //looping through $max iterations, removing an element at random
$rKey = array_rand($foo, 1);
unset($foo[$rKey]);
}
die(print_r($foo));
Use this,
$array = array(); // Your array
$max = 3;
$keys = array_rand($array, count($array) - $max);
// Loop through the generated keys
foreach ($keys as $key) {
unset($array[$key]);
}
If you need to remove an unknown item number, you can do :
$myArr = [
'helping' => 3,
'me' => 2,
'stay' => 1,
'organized!' => 1,
'votre' => 4,
'passion,' => 1,
'action,' => 1,
'et' => 2,
'impact!' => 1,
'being' => 4,
];
$countToRemove = random_int(1, 2); // can return 1 or 2
$keysToRemove = array_rand($myArr, $countToRemove); // returns an int or array
// create the array to loop
foreach (\is_array($keysToRemove) ? $keysToRemove : [$keysToRemove] as $key) {
unset($myArr[$key]);
}
var_dump($myArr); die();
In this case it will randomly remove 1 or 2 items from the array.