Sort array into alternating smallest largest values? - php

Does anyone know how to sort an array into alternating smallest largest values?
I.E.
Array (10, 2, 5, 1, 30, 1, 7)
Should be :
(30, 1, 10, 1, 7, 2, 5)
EDIT:
Forgot to mention the arrays are associative, so:
Array("A"=>10, "B"=>2, "C"=>5, "D"=>1, "E"=>30, "F"=>1, "G"=>7)
Should become:
("E"=>30, "D"=>1, "A"=>10, "F"=>1, "G"=>7, "B"=>2, "C"=>5)

Sort your array then push elements from beginning and end of the array alternatively:
<?php
$myArray = array(10, 2, 5, 1, 30, 1, 7);
sort($myArray );
$count=sizeof($myArray );
$result= array();
for($counter=0; $counter * 2 < $count; $counter++){
array_push($result, $myArray[$count - $counter - 1]);
//check if same elements (when the count is odd)
if ($counter != $count - $counter - 1) {
array_push($result, $myArray[$counter]);
}
}
print_r ($result);
?>
returns:
Array ( [0] => 30 [1] => 1 [2] => 10 [3] => 1 [4] => 7 [5] => 2 [6] => 5 )

<?php
$x = array(10, 2, 5, 1, 30, 1, 7);
// First sort
sort($x);
// Then pick highest and lowest from the back and front of the array
// until it is empty.
$z = array();
while (count($x) > 0){
$z[] = array_pop($x);
if (count($x) > 0) // <- For arrays with an odd number of elements.
$z[] = array_shift($x);
}
var_dump($z);

I can't tell you the exact syntax, my php is very rusty, but what you can do:
Sort your array in descending order
Split in in half, let say array A and B;
Create a new array and add each element from A and B in order $A[i], $B[count($B)-1-i]
This should give you what you need

There is no predefined way to do this. However, php allows for a user sort function usort which you can customise to sort the array in the way you require.

Related

Make new array values from element value plus the previous element's value

I have array like this :
$array = array(1, 4, 8, 3, 7);
I want sum the value of array but first, I unshift the array like this :
<?php
$array = array(1, 4, 8, 3, 7);
array_unshift($array, 0);
array_pop($array);
foreach($array as $key => $val) {
echo $val;
}
?>
then I want sum array first (1, 4, 8, 3, 7) with new array (0, 1, 4, 8, 3)
sum like 1 plus 0 and 4 plus 1 and 8 plus 4 etc
And I want the output is : 1, 5, 12, 11, 10
Just use array_map.
array_map allows you to use a custom function on multiple arrays at the same time
$array = array(1, 4, 8, 3, 7);
$others = $array;
array_unshift($others, 0);
array_pop($others);
function sumarray($v1,$v2){
return $v1+$v2;
}
$res = array_map('sumarray', $array,$others);
print_r($res);
result like
Array
(
[0] => 1
[1] => 5
[2] => 12
[3] => 11
[4] => 10
)
You can use for loop instead of foreach.
Example
$array = $array2 = array(1, 4, 8, 3, 7);
array_unshift($array, 0);
array_pop($array);
$array3 = [];
for($i = 0; $i < count($array); $i++) {
$array3[] = $array[$i]+$array2[$i];
}
print_r($array3);
Output
Array
(
[0] => 1
[1] => 5
[2] => 12
[3] => 11
[4] => 10
)
You can store the popped value in a variable (popped) so you can use it later. Then you can use a regular for loop to loop over your array and add the popped value to the array once the loop is complete:
$array = array(1, 4, 8, 3, 7);
array_unshift($array, 0);
$popped = array_pop($array);
$result = [];
for($i = 0; $i < count($array)-1; $i++) {
$val1 = $array[$i];
$val2 = $array[$i + 1];
$result[] = $val1 + $val2;
}
$result[] = end($array) + $popped; // add the last element with the popped value
print_r($result);
Output:
Array ( [0] => 1 [1] => 5 [2] => 12 [3] => 11 [4] => 10 )
There is no need to pop or shift any elements and there is no need to create a copy of the input array.
As you iterate the input array, sum the current value with the previous value. If there is no previous value, use zero. This is exceedingly simple since your input is an indexed array.
Code: (Demo)
$array = [1, 4, 8, 3, 7];
$result = [];
foreach ($array as $index => $value) {
$result[] = $value + ($array[$index - 1] ?? 0);
}
var_export($result);
// [1, 5, 12, 11, 10]

PHP - Ensure array has certain number of items

I have a PHP array which looks like this...
array
(
[0] => apple,
[1] => orange,
)
I need to ensure the array contains 4 items, so in the instance above I want to end up with this...
array
(
[0] => apple,
[1] => orange,
[2} => ,
[3] => ,
)
Am I best looping through this with a counter and creating a new array, or is there a better method?
Pad your array with elements to a size that you need:
$my_arr = [1,2];
$my_arr = array_pad($my_arr, 4, '');
This should do what you're after
$iNumberOfElements = 5;
$a = array('apple', 'orange');
if(count($a) < $iNumberOfElements){
while (count($a) < $iNumberOfElements) {
$a[] = "";
}
}
var_dump($a);
exit;
as #iainn said: php.net/manual/en/function.array-pad.php
there is this function:
$input = array(12, 10, 9);
$result = array_pad($input, 5, 0);
// result is array(12, 10, 9, 0, 0)
5 is the size of your array, 0 is the default value to empty cells

How to find the median of deepest subarrays of multidimensional array?

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).

Remove every nth item from array

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.

PHP - If any array values match then remove all of them

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;}));

Categories